summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop_todo.yml19
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue27
-rw-r--r--app/assets/javascripts/commons/polyfills.js1
-rw-r--r--app/assets/javascripts/gl_form.js87
-rw-r--r--app/assets/javascripts/helpers/indent_helper.js182
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/success_message.vue4
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js65
-rw-r--r--app/assets/javascripts/lib/utils/keycodes.js10
-rw-r--r--app/assets/javascripts/lib/utils/undo_stack.js105
-rw-r--r--app/assets/javascripts/pages/projects/clusters/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/index.js2
-rw-r--r--app/assets/javascripts/performance_bar/components/performance_bar_app.vue4
-rw-r--r--app/assets/javascripts/projects/gke_cluster_namespace/index.js35
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue41
-rw-r--r--app/assets/stylesheets/framework/common.scss1
-rw-r--r--app/assets/stylesheets/framework/lists.scss16
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb4
-rw-r--r--app/controllers/concerns/group_tree.rb20
-rw-r--r--app/controllers/concerns/sessionless_authentication.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/projects/artifacts_controller.rb5
-rw-r--r--app/controllers/projects/build_artifacts_controller.rb2
-rw-r--r--app/controllers/projects/repositories_controller.rb60
-rw-r--r--app/controllers/sessions_controller.rb11
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/graphql/types/group_type.rb8
-rw-r--r--app/helpers/avatars_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb4
-rw-r--r--app/helpers/submodule_helper.rb2
-rw-r--r--app/mailers/emails/members.rb17
-rw-r--r--app/mailers/notify.rb12
-rw-r--r--app/mailers/previews/notify_preview.rb8
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/models/commit.rb2
-rw-r--r--app/models/concerns/descendant.rb11
-rw-r--r--app/models/concerns/protected_ref.rb2
-rw-r--r--app/models/concerns/relative_positioning.rb52
-rw-r--r--app/models/container_repository.rb5
-rw-r--r--app/models/deployment_metrics.rb13
-rw-r--r--app/models/group.rb3
-rw-r--r--app/models/issue.rb6
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb25
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/models/user_preference.rb2
-rw-r--r--app/policies/group_policy.rb6
-rw-r--r--app/serializers/stage_entity.rb8
-rw-r--r--app/services/ci/process_pipeline_service.rb4
-rw-r--r--app/services/groups/nested_create_service.rb4
-rw-r--r--app/services/groups/transfer_service.rb1
-rw-r--r--app/services/members/destroy_service.rb2
-rw-r--r--app/services/merge_requests/push_options_handler_service.rb17
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb8
-rw-r--r--app/views/clusters/clusters/_namespace.html.haml14
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml9
-rw-r--r--app/views/clusters/platforms/kubernetes/_form.html.haml8
-rw-r--r--app/views/groups/settings/_advanced.html.haml25
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml8
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml4
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml6
-rw-r--r--app/views/projects/tags/show.html.haml2
-rw-r--r--app/views/shared/notes/_hints.html.haml11
-rw-r--r--app/views/shared/projects/_project.html.haml4
-rw-r--r--changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml5
-rw-r--r--changelogs/unreleased/63568-access-email-notifications-custom-email.yml5
-rw-r--r--changelogs/unreleased/64257-warden_set_user_fix.yml5
-rw-r--r--changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml5
-rw-r--r--changelogs/unreleased/64763-fix-tags-page-layout.yml5
-rw-r--r--changelogs/unreleased/64972-fix-unicorn-workers-metric.yml5
-rw-r--r--changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml5
-rw-r--r--changelogs/unreleased/add-caching-to-archive-endpoint.yml5
-rw-r--r--changelogs/unreleased/delete-designs-v2.yml4
-rw-r--r--changelogs/unreleased/dm-submodule-links-nil.yml5
-rw-r--r--changelogs/unreleased/extract_auto_deploy_into_base_image.yml5
-rw-r--r--changelogs/unreleased/je-separate-namespace-fe.yml5
-rw-r--r--changelogs/unreleased/label-descr-push-opts.yml5
-rw-r--r--changelogs/unreleased/mh-editor-indents.yml5
-rw-r--r--changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml6
-rw-r--r--changelogs/unreleased/sh-support-docker-oci-images.yml5
-rw-r--r--config/gitlab.yml.example9
-rw-r--r--config/initializers/0_license.rb2
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/active_record_migration.rb10
-rw-r--r--config/initializers/load_balancing.rb2
-rw-r--r--config/prometheus/cluster_metrics.yml16
-rw-r--r--config/prometheus/common_metrics.yml42
-rw-r--r--db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb2
-rw-r--r--db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb17
-rw-r--r--db/migrate/20190611100202_add_index_to_geo_event_log.rb17
-rw-r--r--db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb25
-rw-r--r--db/migrate/20190709220014_import_common_metrics_y_axis.rb13
-rw-r--r--db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb27
-rw-r--r--db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb30
-rw-r--r--db/schema.rb15
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/logs.md2
-rw-r--r--doc/api/README.md7
-rw-r--r--doc/api/dependencies.md4
-rw-r--r--doc/api/group_clusters.md2
-rw-r--r--doc/api/project_clusters.md2
-rw-r--r--doc/ci/README.md6
-rw-r--r--doc/ci/introduction/index.md5
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md36
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.pngbin0 -> 63661 bytes
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.pngbin0 -> 30760 bytes
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md69
-rw-r--r--doc/ci/pipelines.md5
-rw-r--r--doc/development/README.md4
-rw-r--r--doc/development/code_review.md25
-rw-r--r--doc/development/database_review.md11
-rw-r--r--doc/development/documentation/index.md43
-rw-r--r--doc/development/documentation/styleguide.md2
-rw-r--r--doc/development/filtering_by_label.md166
-rw-r--r--doc/development/new_fe_guide/dependencies.md12
-rw-r--r--doc/development/testing_guide/frontend_testing.md2
-rw-r--r--doc/install/azure/index.md7
-rw-r--r--doc/integration/README.md4
-rw-r--r--doc/integration/slack.md4
-rw-r--r--doc/topics/autodevops/index.md92
-rw-r--r--doc/university/README.md4
-rw-r--r--doc/user/admin_area/abuse_reports.md16
-rw-r--r--doc/user/admin_area/broadcast_messages.md16
-rw-r--r--doc/user/admin_area/custom_project_templates.md45
-rw-r--r--doc/user/application_security/container_scanning/index.md8
-rw-r--r--doc/user/application_security/dast/index.md16
-rw-r--r--doc/user/application_security/dependency_scanning/index.md20
-rw-r--r--doc/user/application_security/index.md38
-rw-r--r--doc/user/application_security/license_management/index.md16
-rw-r--r--doc/user/application_security/sast/index.md16
-rw-r--r--doc/user/application_security/security_dashboard/img/dashboard.pngbin58585 -> 0 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/group_security_dashboard.pngbin0 -> 226261 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/project_security_dashboard.pngbin126356 -> 166559 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md26
-rw-r--r--doc/user/clusters/applications.md3
-rw-r--r--doc/user/group/saml_sso/img/scim_attribute_mapping.pngbin95420 -> 113191 bytes
-rw-r--r--doc/user/group/saml_sso/img/scim_name_identifier_mapping.pngbin0 -> 175281 bytes
-rw-r--r--doc/user/group/saml_sso/img/scim_provisioning_status.pngbin0 -> 23006 bytes
-rw-r--r--doc/user/group/saml_sso/scim_setup.md51
-rw-r--r--doc/user/markdown.md66
-rw-r--r--doc/user/profile/index.md31
-rw-r--r--doc/user/project/clusters/index.md414
-rw-r--r--doc/user/project/clusters/runbooks/index.md4
-rw-r--r--doc/user/project/clusters/serverless/index.md5
-rw-r--r--doc/user/project/container_registry.md71
-rw-r--r--doc/user/project/img/container_registry.pngbin35202 -> 0 bytes
-rw-r--r--doc/user/project/integrations/prometheus.md5
-rw-r--r--doc/user/project/merge_requests/index.md24
-rw-r--r--doc/user/project/protected_branches.md1
-rw-r--r--lib/api/entities.rb5
-rw-r--r--lib/api/groups.rb5
-rw-r--r--lib/api/job_artifacts.rb4
-rw-r--r--lib/api/validations/types/labels_list.rb2
-rw-r--r--lib/banzai/reference_redactor.rb2
-rw-r--r--lib/container_registry/client.rb9
-rw-r--r--lib/gitlab/auth/activity.rb7
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb3
-rw-r--r--lib/gitlab/background_migration/backfill_project_repositories.rb2
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb2
-rw-r--r--lib/gitlab/badge/coverage/report.rb2
-rw-r--r--lib/gitlab/ci/config/normalizer.rb2
-rw-r--r--lib/gitlab/ci/status/factory.rb6
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml421
-rw-r--r--lib/gitlab/database/subquery.rb6
-rw-r--r--lib/gitlab/object_hierarchy.rb17
-rw-r--r--lib/gitlab/profiler.rb2
-rw-r--r--lib/gitlab/project_authorizations.rb121
-rw-r--r--lib/gitlab/project_authorizations/with_nested_groups.rb125
-rw-r--r--lib/gitlab/project_authorizations/without_nested_groups.rb35
-rw-r--r--lib/gitlab/push_options.rb4
-rw-r--r--lib/gitlab/regex.rb6
-rw-r--r--lib/gitlab/sidekiq_middleware/memory_killer.rb23
-rw-r--r--lib/gitlab/submodule_links.rb11
-rw-r--r--lib/gitlab/utils.rb2
-rw-r--r--lib/prometheus/pid_provider.rb35
-rw-r--r--lib/tasks/gitlab/db.rake2
-rw-r--r--locale/gitlab.pot63
-rw-r--r--package.json4
-rw-r--r--qa/qa/page/project/issue/show.rb5
-rw-r--r--qa/qa/page/project/new.rb2
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb21
-rw-r--r--qa/qa/page/project/sub_menus/project.rb4
-rw-r--r--qa/qa/resource/project.rb6
-rw-r--r--qa/spec/spec_helper.rb2
-rwxr-xr-xscripts/insert-rspec-profiling-data4
-rwxr-xr-xscripts/review_apps/review-apps.sh2
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb2
-rw-r--r--spec/controllers/concerns/group_tree_spec.rb4
-rw-r--r--spec/controllers/dashboard/groups_controller_spec.rb2
-rw-r--r--spec/controllers/groups/children_controller_spec.rb4
-rw-r--r--spec/controllers/groups/labels_controller_spec.rb4
-rw-r--r--spec/controllers/groups_controller_spec.rb6
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb6
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb6
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb5
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/projects/repositories_controller_spec.rb47
-rw-r--r--spec/factories/abuse_reports.rb2
-rw-r--r--spec/factories/appearances.rb2
-rw-r--r--spec/factories/application_settings.rb2
-rw-r--r--spec/factories/award_emoji.rb2
-rw-r--r--spec/factories/badge.rb2
-rw-r--r--spec/factories/boards.rb2
-rw-r--r--spec/factories/broadcast_messages.rb2
-rw-r--r--spec/factories/chat_names.rb2
-rw-r--r--spec/factories/chat_teams.rb2
-rw-r--r--spec/factories/ci/bridge.rb2
-rw-r--r--spec/factories/ci/build_trace_chunks.rb2
-rw-r--r--spec/factories/ci/build_trace_section_names.rb2
-rw-r--r--spec/factories/ci/builds.rb2
-rw-r--r--spec/factories/ci/group_variables.rb2
-rw-r--r--spec/factories/ci/job_artifacts.rb2
-rw-r--r--spec/factories/ci/pipeline_schedule.rb2
-rw-r--r--spec/factories/ci/pipeline_schedule_variables.rb2
-rw-r--r--spec/factories/ci/pipeline_variables.rb2
-rw-r--r--spec/factories/ci/pipelines.rb2
-rw-r--r--spec/factories/ci/runner_projects.rb2
-rw-r--r--spec/factories/ci/runners.rb2
-rw-r--r--spec/factories/ci/stages.rb2
-rw-r--r--spec/factories/ci/trigger_requests.rb2
-rw-r--r--spec/factories/ci/triggers.rb2
-rw-r--r--spec/factories/ci/variables.rb2
-rw-r--r--spec/factories/clusters/applications/helm.rb2
-rw-r--r--spec/factories/clusters/clusters.rb2
-rw-r--r--spec/factories/clusters/platforms/kubernetes.rb2
-rw-r--r--spec/factories/clusters/providers/gcp.rb2
-rw-r--r--spec/factories/commit_statuses.rb2
-rw-r--r--spec/factories/commits.rb2
-rw-r--r--spec/factories/container_repositories.rb2
-rw-r--r--spec/factories/conversational_development_index_metrics.rb2
-rw-r--r--spec/factories/deploy_keys_projects.rb2
-rw-r--r--spec/factories/deploy_tokens.rb2
-rw-r--r--spec/factories/deployments.rb2
-rw-r--r--spec/factories/emails.rb2
-rw-r--r--spec/factories/environments.rb2
-rw-r--r--spec/factories/events.rb2
-rw-r--r--spec/factories/file_uploaders.rb2
-rw-r--r--spec/factories/fork_network_members.rb2
-rw-r--r--spec/factories/fork_networks.rb2
-rw-r--r--spec/factories/gitaly/commit.rb2
-rw-r--r--spec/factories/gitaly/commit_author.rb2
-rw-r--r--spec/factories/gitaly/tag.rb2
-rw-r--r--spec/factories/gpg_key_subkeys.rb2
-rw-r--r--spec/factories/gpg_keys.rb2
-rw-r--r--spec/factories/gpg_signature.rb2
-rw-r--r--spec/factories/group_custom_attributes.rb2
-rw-r--r--spec/factories/group_members.rb2
-rw-r--r--spec/factories/groups.rb2
-rw-r--r--spec/factories/identities.rb2
-rw-r--r--spec/factories/import_export_uploads.rb2
-rw-r--r--spec/factories/import_states.rb2
-rw-r--r--spec/factories/instance_configuration.rb2
-rw-r--r--spec/factories/internal_ids.rb2
-rw-r--r--spec/factories/issues.rb2
-rw-r--r--spec/factories/keys.rb2
-rw-r--r--spec/factories/label_links.rb2
-rw-r--r--spec/factories/label_priorities.rb2
-rw-r--r--spec/factories/labels.rb2
-rw-r--r--spec/factories/lfs_file_locks.rb2
-rw-r--r--spec/factories/lfs_objects.rb2
-rw-r--r--spec/factories/lfs_objects_projects.rb2
-rw-r--r--spec/factories/lists.rb2
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/merge_requests_closing_issues.rb2
-rw-r--r--spec/factories/milestones.rb2
-rw-r--r--spec/factories/namespaces.rb2
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/notification_settings.rb2
-rw-r--r--spec/factories/oauth_access_grants.rb2
-rw-r--r--spec/factories/oauth_access_tokens.rb2
-rw-r--r--spec/factories/oauth_applications.rb2
-rw-r--r--spec/factories/pages_domains.rb2
-rw-r--r--spec/factories/personal_access_tokens.rb2
-rw-r--r--spec/factories/pool_repositories.rb2
-rw-r--r--spec/factories/programming_languages.rb2
-rw-r--r--spec/factories/project_auto_devops.rb2
-rw-r--r--spec/factories/project_custom_attributes.rb2
-rw-r--r--spec/factories/project_deploy_tokens.rb2
-rw-r--r--spec/factories/project_group_links.rb2
-rw-r--r--spec/factories/project_hooks.rb2
-rw-r--r--spec/factories/project_members.rb2
-rw-r--r--spec/factories/project_statistics.rb2
-rw-r--r--spec/factories/project_wikis.rb2
-rw-r--r--spec/factories/projects.rb2
-rw-r--r--spec/factories/protected_branches.rb2
-rw-r--r--spec/factories/protected_tags.rb2
-rw-r--r--spec/factories/redirect_routes.rb2
-rw-r--r--spec/factories/releases.rb2
-rw-r--r--spec/factories/remote_mirrors.rb2
-rw-r--r--spec/factories/repository_languages.rb2
-rw-r--r--spec/factories/sent_notifications.rb2
-rw-r--r--spec/factories/sequences.rb2
-rw-r--r--spec/factories/service_hooks.rb2
-rw-r--r--spec/factories/services.rb2
-rw-r--r--spec/factories/shards.rb2
-rw-r--r--spec/factories/snippets.rb2
-rw-r--r--spec/factories/spam_logs.rb2
-rw-r--r--spec/factories/subscriptions.rb2
-rw-r--r--spec/factories/system_hooks.rb2
-rw-r--r--spec/factories/system_note_metadata.rb2
-rw-r--r--spec/factories/term_agreements.rb2
-rw-r--r--spec/factories/terms.rb2
-rw-r--r--spec/factories/timelogs.rb2
-rw-r--r--spec/factories/todos.rb2
-rw-r--r--spec/factories/trending_project.rb2
-rw-r--r--spec/factories/u2f_registrations.rb2
-rw-r--r--spec/factories/uploads.rb2
-rw-r--r--spec/factories/user_agent_details.rb2
-rw-r--r--spec/factories/user_callouts.rb2
-rw-r--r--spec/factories/user_custom_attributes.rb2
-rw-r--r--spec/factories/users.rb2
-rw-r--r--spec/factories/users_star_projects.rb2
-rw-r--r--spec/factories/web_hook_log.rb2
-rw-r--r--spec/factories/wiki_directories.rb2
-rw-r--r--spec/factories/wiki_pages.rb2
-rw-r--r--spec/features/dashboard/groups_list_spec.rb6
-rw-r--r--spec/features/groups/empty_states_spec.rb2
-rw-r--r--spec/features/groups/group_settings_spec.rb2
-rw-r--r--spec/features/groups/issues_spec.rb2
-rw-r--r--spec/features/groups/members/list_members_spec.rb4
-rw-r--r--spec/features/groups/share_lock_spec.rb2
-rw-r--r--spec/features/groups/show_spec.rb38
-rw-r--r--spec/features/groups_spec.rb4
-rw-r--r--spec/features/ide_spec.rb2
-rw-r--r--spec/features/import/manifest_import_spec.rb2
-rw-r--r--spec/features/labels_hierarchy_spec.rb2
-rw-r--r--spec/features/markdown/metrics_spec.rb60
-rw-r--r--spec/features/oauth_login_spec.rb1
-rw-r--r--spec/features/projects/artifacts/user_downloads_artifacts_spec.rb4
-rw-r--r--spec/features/projects/commits/user_browses_commits_spec.rb2
-rw-r--r--spec/features/projects/members/invite_group_spec.rb4
-rw-r--r--spec/features/projects/new_project_spec.rb2
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb2
-rw-r--r--spec/features/projects/settings/user_transfers_a_project_spec.rb2
-rw-r--r--spec/features/projects/sub_group_issuables_spec.rb2
-rw-r--r--spec/features/projects/user_creates_project_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb12
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/features/security/project/private_access_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb38
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb13
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb2
-rw-r--r--spec/finders/cluster_ancestors_finder_spec.rb4
-rw-r--r--spec/finders/group_descendants_finder_spec.rb6
-rw-r--r--spec/finders/group_members_finder_spec.rb4
-rw-r--r--spec/finders/group_projects_finder_spec.rb12
-rw-r--r--spec/finders/groups_finder_spec.rb2
-rw-r--r--spec/finders/issues_finder_spec.rb2
-rw-r--r--spec/finders/labels_finder_spec.rb6
-rw-r--r--spec/finders/members_finder_spec.rb8
-rw-r--r--spec/finders/merge_requests_finder_spec.rb2
-rw-r--r--spec/finders/todos_finder_spec.rb2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json6
-rw-r--r--spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json4
-rw-r--r--spec/frontend/helpers/indent_helper_spec.js371
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js180
-rw-r--r--spec/frontend/lib/utils/undo_stack_spec.js237
-rw-r--r--spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js61
-rw-r--r--spec/graphql/resolvers/namespace_projects_resolver_spec.rb2
-rw-r--r--spec/helpers/auto_devops_helper_spec.rb6
-rw-r--r--spec/helpers/groups_helper_spec.rb6
-rw-r--r--spec/helpers/namespaces_helper_spec.rb4
-rw-r--r--spec/helpers/submodule_helper_spec.rb13
-rw-r--r--spec/javascripts/badges/components/badge_list_spec.js2
-rw-r--r--spec/javascripts/badges/components/badge_spec.js2
-rw-r--r--spec/javascripts/boards/board_list_spec.js2
-rw-r--r--spec/javascripts/registry/components/app_spec.js2
-rw-r--r--spec/javascripts/reports/components/grouped_test_reports_app_spec.js12
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js2
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb4
-rw-r--r--spec/lib/container_registry/client_spec.rb36
-rw-r--r--spec/lib/container_registry/tag_spec.rb2
-rw-r--r--spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb7
-rw-r--r--spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb2
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb16
-rw-r--r--spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/database/grant_spec.rb2
-rw-r--r--spec/lib/gitlab/group_search_results_spec.rb6
-rw-r--r--spec/lib/gitlab/manifest_import/manifest_spec.rb2
-rw-r--r--spec/lib/gitlab/manifest_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/object_hierarchy_spec.rb2
-rw-r--r--spec/lib/gitlab/performance_bar_spec.rb4
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb38
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb3
-rw-r--r--spec/lib/gitlab/sql/cte_spec.rb2
-rw-r--r--spec/lib/gitlab/sql/recursive_cte_spec.rb2
-rw-r--r--spec/lib/gitlab/submodule_links_spec.rb47
-rw-r--r--spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb73
-rw-r--r--spec/lib/prometheus/pid_provider_spec.rb66
-rw-r--r--spec/mailers/notify_spec.rb21
-rw-r--r--spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb21
-rw-r--r--spec/models/ci/pipeline_spec.rb17
-rw-r--r--spec/models/ci/runner_spec.rb2
-rw-r--r--spec/models/clusters/cluster_spec.rb2
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb2
-rw-r--r--spec/models/concerns/group_descendant_spec.rb6
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/concerns/relative_positioning_spec.rb242
-rw-r--r--spec/models/container_repository_spec.rb2
-rw-r--r--spec/models/deployment_metrics_spec.rb12
-rw-r--r--spec/models/group_spec.rb28
-rw-r--r--spec/models/issue_spec.rb8
-rw-r--r--spec/models/members/group_member_spec.rb2
-rw-r--r--spec/models/members/project_member_spec.rb2
-rw-r--r--spec/models/namespace/root_storage_statistics_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb20
-rw-r--r--spec/models/notification_recipient_spec.rb2
-rw-r--r--spec/models/postgresql/replication_slot_spec.rb2
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_spec.rb101
-rw-r--r--spec/models/todo_spec.rb6
-rw-r--r--spec/models/user_spec.rb38
-rw-r--r--spec/policies/group_member_policy_spec.rb2
-rw-r--r--spec/policies/group_policy_spec.rb59
-rw-r--r--spec/presenters/clusters/cluster_presenter_spec.rb2
-rw-r--r--spec/requests/api/boards_spec.rb2
-rw-r--r--spec/requests/api/graphql/namespace/projects_spec.rb2
-rw-r--r--spec/requests/api/group_labels_spec.rb4
-rw-r--r--spec/requests/api/groups_spec.rb10
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb2
-rw-r--r--spec/requests/api/members_spec.rb6
-rw-r--r--spec/requests/api/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/resource_label_events_spec.rb45
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/serializers/group_child_entity_spec.rb2
-rw-r--r--spec/serializers/group_child_serializer_spec.rb4
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb2
-rw-r--r--spec/services/boards/issues/list_service_spec.rb2
-rw-r--r--spec/services/groups/auto_devops_service_spec.rb2
-rw-r--r--spec/services/groups/create_service_spec.rb46
-rw-r--r--spec/services/groups/nested_create_service_spec.rb30
-rw-r--r--spec/services/groups/transfer_service_spec.rb17
-rw-r--r--spec/services/groups/update_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb4
-rw-r--r--spec/services/members/destroy_service_spec.rb6
-rw-r--r--spec/services/merge_requests/push_options_handler_service_spec.rb154
-rw-r--r--spec/services/notification_service_spec.rb50
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb2
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb4
-rw-r--r--spec/services/todos/destroy/entity_leave_service_spec.rb2
-rw-r--r--spec/services/todos/destroy/group_private_service_spec.rb2
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb4
-rw-r--r--spec/spec_helper.rb8
-rw-r--r--spec/support/api/boards_shared_examples.rb2
-rw-r--r--spec/support/api/issues_resolving_discussions_shared_examples.rb2
-rw-r--r--spec/support/api/members_shared_examples.rb2
-rw-r--r--spec/support/api/milestones_shared_examples.rb2
-rw-r--r--spec/support/api/repositories_shared_context.rb2
-rw-r--r--spec/support/api/schema_matcher.rb2
-rw-r--r--spec/support/api/scopes/read_user_shared_examples.rb2
-rw-r--r--spec/support/api/time_tracking_shared_examples.rb2
-rw-r--r--spec/support/banzai/reference_filter_shared_examples.rb2
-rw-r--r--spec/support/batch_loader.rb2
-rw-r--r--spec/support/capybara.rb2
-rw-r--r--spec/support/carrierwave.rb2
-rw-r--r--spec/support/chunked_io/chunked_io_helpers.rb2
-rw-r--r--spec/support/commit_trailers_spec_helper.rb2
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_context.rb2
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb8
-rw-r--r--spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb2
-rw-r--r--spec/support/controllers/sessionless_auth_controller_shared_examples.rb2
-rw-r--r--spec/support/cycle_analytics_helpers/test_generation.rb2
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/external_authorization_service_helpers.rb2
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb2
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb2
-rw-r--r--spec/support/features/resolving_discussions_in_issues_shared_examples.rb2
-rw-r--r--spec/support/features/rss_shared_examples.rb2
-rw-r--r--spec/support/features/variable_list_shared_examples.rb2
-rw-r--r--spec/support/forgery_protection.rb2
-rw-r--r--spec/support/google_api/cloud_platform_helpers.rb2
-rw-r--r--spec/support/helpers/api_helpers.rb11
-rw-r--r--spec/support/helpers/assets_helpers.rb2
-rw-r--r--spec/support/helpers/bare_repo_operations.rb2
-rw-r--r--spec/support/helpers/board_helpers.rb2
-rw-r--r--spec/support/helpers/capybara_helpers.rb2
-rw-r--r--spec/support/helpers/ci_artifact_metadata_generator.rb2
-rw-r--r--spec/support/helpers/cookie_helper.rb2
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/helpers/database_connection_helpers.rb2
-rw-r--r--spec/support/helpers/devise_helpers.rb2
-rw-r--r--spec/support/helpers/drag_to_helper.rb2
-rw-r--r--spec/support/helpers/dropzone_helper.rb2
-rw-r--r--spec/support/helpers/email_helpers.rb2
-rw-r--r--spec/support/helpers/exclusive_lease_helpers.rb2
-rw-r--r--spec/support/helpers/expect_next_instance_of.rb2
-rw-r--r--spec/support/helpers/expect_offense.rb2
-rw-r--r--spec/support/helpers/fake_blob_helpers.rb2
-rw-r--r--spec/support/helpers/fake_migration_classes.rb2
-rw-r--r--spec/support/helpers/fake_u2f_device.rb2
-rw-r--r--spec/support/helpers/features/branches_helpers.rb2
-rw-r--r--spec/support/helpers/features/notes_helpers.rb2
-rw-r--r--spec/support/helpers/features/sorting_helpers.rb2
-rw-r--r--spec/support/helpers/filter_spec_helper.rb2
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb2
-rw-r--r--spec/support/helpers/fixture_helpers.rb2
-rw-r--r--spec/support/helpers/git_http_helpers.rb2
-rw-r--r--spec/support/helpers/gitlab_verify_helpers.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb2
-rw-r--r--spec/support/helpers/import_spec_helper.rb2
-rw-r--r--spec/support/helpers/input_helper.rb2
-rw-r--r--spec/support/helpers/inspect_requests.rb2
-rw-r--r--spec/support/helpers/issue_helpers.rb2
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb2
-rw-r--r--spec/support/helpers/jira_service_helper.rb2
-rw-r--r--spec/support/helpers/key_generator_helper.rb2
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb2
-rw-r--r--spec/support/helpers/ldap_helpers.rb2
-rw-r--r--spec/support/helpers/live_debugger.rb2
-rw-r--r--spec/support/helpers/login_helpers.rb2
-rw-r--r--spec/support/helpers/markdown_feature.rb2
-rw-r--r--spec/support/helpers/merge_request_diff_helpers.rb2
-rw-r--r--spec/support/helpers/merge_request_helpers.rb2
-rw-r--r--spec/support/helpers/migrations_helpers.rb2
-rw-r--r--spec/support/helpers/mobile_helpers.rb2
-rw-r--r--spec/support/helpers/note_interaction_helpers.rb2
-rw-r--r--spec/support/helpers/notification_helpers.rb2
-rw-r--r--spec/support/helpers/project_forks_helper.rb2
-rw-r--r--spec/support/helpers/prometheus_helpers.rb10
-rw-r--r--spec/support/helpers/query_recorder.rb2
-rw-r--r--spec/support/helpers/quick_actions_helpers.rb2
-rw-r--r--spec/support/helpers/rake_helpers.rb2
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb2
-rw-r--r--spec/support/helpers/redis_without_keys.rb2
-rw-r--r--spec/support/helpers/reference_parser_helpers.rb2
-rw-r--r--spec/support/helpers/repo_helpers.rb2
-rw-r--r--spec/support/helpers/routes_helpers.rb2
-rw-r--r--spec/support/helpers/search_helpers.rb2
-rw-r--r--spec/support/helpers/seed_repo.rb2
-rw-r--r--spec/support/helpers/select2_helper.rb2
-rw-r--r--spec/support/helpers/selection_helper.rb2
-rw-r--r--spec/support/helpers/sorting_helper.rb2
-rw-r--r--spec/support/helpers/stub_configuration.rb2
-rw-r--r--spec/support/helpers/stub_env.rb2
-rw-r--r--spec/support/helpers/stub_feature_flags.rb2
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb2
-rw-r--r--spec/support/helpers/stub_gitlab_data.rb2
-rw-r--r--spec/support/helpers/stub_metrics.rb2
-rw-r--r--spec/support/helpers/stub_object_storage.rb2
-rw-r--r--spec/support/helpers/stub_requests.rb2
-rw-r--r--spec/support/helpers/stub_worker.rb2
-rw-r--r--spec/support/helpers/terms_helper.rb2
-rw-r--r--spec/support/helpers/test_env.rb2
-rw-r--r--spec/support/helpers/upload_helpers.rb2
-rw-r--r--spec/support/helpers/wait_for_requests.rb2
-rw-r--r--spec/support/helpers/wait_helpers.rb2
-rw-r--r--spec/support/helpers/wiki_helpers.rb2
-rw-r--r--spec/support/helpers/workhorse_helpers.rb2
-rw-r--r--spec/support/http_io/http_io_helpers.rb2
-rw-r--r--spec/support/import_export/common_util.rb2
-rw-r--r--spec/support/import_export/configuration_helper.rb2
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/inspect_squelch.rb2
-rw-r--r--spec/support/issuables_requiring_filter_shared_examples.rb2
-rw-r--r--spec/support/json_response.rb2
-rw-r--r--spec/support/matchers/abort_matcher.rb2
-rw-r--r--spec/support/matchers/access_matchers.rb2
-rw-r--r--spec/support/matchers/access_matchers_for_controller.rb2
-rw-r--r--spec/support/matchers/background_migrations_matchers.rb2
-rw-r--r--spec/support/matchers/be_a_binary_string.rb2
-rw-r--r--spec/support/matchers/be_like_time.rb2
-rw-r--r--spec/support/matchers/be_url.rb2
-rw-r--r--spec/support/matchers/be_utf8.rb2
-rw-r--r--spec/support/matchers/be_valid_commit.rb2
-rw-r--r--spec/support/matchers/disallow_request_matchers.rb2
-rw-r--r--spec/support/matchers/exceed_query_limit.rb2
-rw-r--r--spec/support/matchers/execute_check.rb2
-rw-r--r--spec/support/matchers/gitaly_matchers.rb2
-rw-r--r--spec/support/matchers/gitlab_git_matchers.rb2
-rw-r--r--spec/support/matchers/graphql_matchers.rb2
-rw-r--r--spec/support/matchers/have_emoji.rb2
-rw-r--r--spec/support/matchers/have_gitlab_http_status.rb2
-rw-r--r--spec/support/matchers/have_issuable_counts.rb2
-rw-r--r--spec/support/matchers/include_module.rb2
-rw-r--r--spec/support/matchers/issuable_matchers.rb2
-rw-r--r--spec/support/matchers/markdown_matchers.rb2
-rw-r--r--spec/support/matchers/match_file.rb2
-rw-r--r--spec/support/matchers/match_ids.rb2
-rw-r--r--spec/support/matchers/metric_counter_matcher.rb2
-rw-r--r--spec/support/matchers/navigation_matcher.rb2
-rw-r--r--spec/support/matchers/pagination_matcher.rb2
-rw-r--r--spec/support/matchers/query_matcher.rb2
-rw-r--r--spec/support/matchers/satisfy_matchers.rb2
-rw-r--r--spec/support/matchers/security_header_matcher.rb2
-rw-r--r--spec/support/migrations_helpers/track_untracked_uploads_helpers.rb2
-rw-r--r--spec/support/omni_auth.rb2
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb2
-rw-r--r--spec/support/prometheus/metric_builders.rb2
-rw-r--r--spec/support/protected_tags/access_control_ce_shared_examples.rb2
-rw-r--r--spec/support/redis/redis_helpers.rb2
-rw-r--r--spec/support/redis/redis_shared_examples.rb2
-rw-r--r--spec/support/rspec.rb2
-rw-r--r--spec/support/seed.rb2
-rw-r--r--spec/support/services/clusters/create_service_shared.rb2
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb2
-rw-r--r--spec/support/services/issuable_update_service_shared_examples.rb2
-rw-r--r--spec/support/services/migrate_to_ghost_user_service_shared_examples.rb2
-rw-r--r--spec/support/setup_builds_storage.rb2
-rw-r--r--spec/support/shared_contexts/email_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb2
-rw-r--r--spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb2
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb2
-rw-r--r--spec/support/shared_contexts/finders/users_finder_shared_contexts.rb2
-rw-r--r--spec/support/shared_contexts/json_response_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/merge_requests_allowing_collaboration.rb2
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/services_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/url_shared_context.rb2
-rw-r--r--spec/support/shared_examples/chat_slash_commands_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/ci_trace_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/common_system_notes_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/todos_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/variables_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/dirty_submit_form_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/email_format_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/fast_destroy_all.rb2
-rw-r--r--spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb2
-rw-r--r--spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce.rb2
-rw-r--r--spec/support/shared_examples/features/search_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/file_finder.rb2
-rw-r--r--spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb2
-rw-r--r--spec/support/shared_examples/gitlab_verify.rb2
-rw-r--r--spec/support/shared_examples/graphql/issuable_state_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/group_members_shared_example.rb2
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb2
-rw-r--r--spec/support/shared_examples/issuable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/issuables_list_metadata_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/issue_tracker_service_shared_example.rb2
-rw-r--r--spec/support/shared_examples/ldap_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/legacy_path_redirect_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/a_redis_counter.rb57
-rw-r--r--spec/support/shared_examples/malicious_regexp_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/mentionable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/milestone_tabs_examples.rb2
-rw-r--r--spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/chat_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/cluster_application_core_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb2
-rw-r--r--spec/support/shared_examples/models/cluster_application_status_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb2
-rw-r--r--spec/support/shared_examples/models/project_hook_data_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/with_uploads_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/position_formatters.rb2
-rw-r--r--spec/support/shared_examples/project_latest_successful_build_for_examples.rb63
-rw-r--r--spec/support/shared_examples/reference_parser_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/relative_positioning_shared_examples.rb253
-rw-r--r--spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/diff_discussions.rb2
-rw-r--r--spec/support/shared_examples/requests/api/discussions.rb2
-rw-r--r--spec/support/shared_examples/requests/api/issuable_participants_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/notes.rb2
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions.rb2
-rw-r--r--spec/support/shared_examples/requests/api/status_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/resource_label_events_api.rb44
-rw-r--r--spec/support/shared_examples/serializers/note_entity_examples.rb2
-rw-r--r--spec/support/shared_examples/services/boards/boards_create_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/boards_list_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/issues_list_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_destroy_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_list_service.rb2
-rw-r--r--spec/support/shared_examples/services/boards/lists_move_service.rb2
-rw-r--r--spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/snippet_visibility_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/snippets_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/taskable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/throttled_touch.rb2
-rw-r--r--spec/support/shared_examples/unique_ip_check_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/update_invalid_issuable.rb2
-rw-r--r--spec/support/shared_examples/updating_mentions_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/uploaders/object_storage_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/url_validator_examples.rb2
-rw-r--r--spec/support/sidekiq.rb2
-rw-r--r--spec/support/stored_repositories.rb2
-rw-r--r--spec/support/test_reports/test_reports_helper.rb2
-rw-r--r--spec/support/trace/trace_helpers.rb2
-rw-r--r--spec/support/webmock.rb2
-rw-r--r--spec/views/groups/edit.html.haml_spec.rb2
-rw-r--r--spec/views/layouts/header/_new_dropdown.haml_spec.rb2
-rw-r--r--spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb1
-rw-r--r--spec/views/projects/merge_requests/edit.html.haml_spec.rb1
-rw-r--r--yarn.lock10
711 files changed, 5222 insertions, 2331 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 41714eefa97..3898206e3b5 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -262,25 +262,6 @@ Naming/HeredocDelimiterNaming:
Naming/RescuedExceptionsVariableName:
Enabled: false
-# Offense count: 6
-# Cop supports --auto-correct.
-Performance/InefficientHashSearch:
- Exclude:
- - 'app/controllers/concerns/sessionless_authentication.rb'
- - 'app/models/note.rb'
- - 'app/models/user_preference.rb'
- - 'ee/app/models/ee/project.rb'
- - 'lib/gitlab/import_export/members_mapper.rb'
- - 'qa/spec/spec_helper.rb'
-
-# Offense count: 3
-# Cop supports --auto-correct.
-Performance/ReverseEach:
- Exclude:
- - 'app/models/commit.rb'
- - 'db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb'
- - 'lib/gitlab/profiler.rb'
-
# Offense count: 7081
# Configuration parameters: Prefixes.
# Prefixes: when, with, without
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index 6754abf4019..ebf48cee2ae 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -1,4 +1,5 @@
<script>
+import { __ } from '~/locale';
import Flash from '~/flash';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { visitUrl } from '~/lib/utils/url_utility';
@@ -86,12 +87,12 @@ export default {
},
buttonText() {
if (this.isNewForm) {
- return 'Create board';
+ return __('Create board');
}
if (this.isDeleteForm) {
- return 'Delete';
+ return __('Delete');
}
- return 'Save changes';
+ return __('Save changes');
},
buttonKind() {
if (this.isNewForm) {
@@ -104,15 +105,15 @@ export default {
},
title() {
if (this.isNewForm) {
- return 'Create new board';
+ return __('Create new board');
}
if (this.isDeleteForm) {
- return 'Delete board';
+ return __('Delete board');
}
if (this.readonly) {
- return 'Board scope';
+ return __('Board scope');
}
- return 'Edit board';
+ return __('Edit board');
},
readonly() {
return !this.canAdminBoard;
@@ -138,7 +139,7 @@ export default {
visitUrl(boardsStore.rootPath);
})
.catch(() => {
- Flash('Failed to delete board. Please try again.');
+ Flash(__('Failed to delete board. Please try again.'));
this.isLoading = false;
});
} else {
@@ -149,7 +150,7 @@ export default {
visitUrl(data.board_path);
})
.catch(() => {
- Flash('Unable to save your changes. Please try again.');
+ Flash(__('Unable to save your changes. Please try again.'));
this.isLoading = false;
});
}
@@ -182,17 +183,19 @@ export default {
@submit="submit"
>
<template slot="body">
- <p v-if="isDeleteForm">Are you sure you want to delete this board?</p>
+ <p v-if="isDeleteForm">{{ __('Are you sure you want to delete this board?') }}</p>
<form v-else class="js-board-config-modal" @submit.prevent>
<div v-if="!readonly" class="append-bottom-20">
- <label class="form-section-title label-bold" for="board-new-name"> Board name </label>
+ <label class="form-section-title label-bold" for="board-new-name">{{
+ __('Board name')
+ }}</label>
<input
id="board-new-name"
ref="name"
v-model="board.name"
class="form-control"
type="text"
- placeholder="Enter board name"
+ :placeholder="__('Enter board name')"
@keyup.enter="submit"
/>
</div>
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index 7a6ad3dc771..daa941a63cd 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -12,6 +12,7 @@ import 'core-js/es/promise/finally';
import 'core-js/es/string/code-point-at';
import 'core-js/es/string/from-code-point';
import 'core-js/es/string/includes';
+import 'core-js/es/string/repeat';
import 'core-js/es/string/starts-with';
import 'core-js/es/string/ends-with';
import 'core-js/es/symbol';
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index a66555838ba..b98fe9f6ce2 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -3,9 +3,16 @@ import autosize from 'autosize';
import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
import dropzoneInput from './dropzone_input';
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
+import IndentHelper from './helpers/indent_helper';
+import { keystroke } from './lib/utils/common_utils';
+import * as keys from './lib/utils/keycodes';
+import UndoStack from './lib/utils/undo_stack';
export default class GLForm {
constructor(form, enableGFM = {}) {
+ this.handleKeyShortcuts = this.handleKeyShortcuts.bind(this);
+ this.setState = this.setState.bind(this);
+
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
this.enableGFM = Object.assign({}, defaultAutocompleteConfig, enableGFM);
@@ -16,6 +23,10 @@ export default class GLForm {
this.enableGFM[item] = Boolean(dataSources[item]);
}
});
+
+ this.undoStack = new UndoStack();
+ this.indentHelper = new IndentHelper(this.textarea[0]);
+
// Before we start, we should clean up any previous data for this form
this.destroy();
// Set up the form
@@ -85,9 +96,84 @@ export default class GLForm {
clearEventListeners() {
this.textarea.off('focus');
this.textarea.off('blur');
+ this.textarea.off('keydown');
removeMarkdownListeners(this.form);
}
+ setState(state) {
+ const selection = [this.textarea[0].selectionStart, this.textarea[0].selectionEnd];
+ this.textarea.val(state);
+ this.textarea[0].setSelectionRange(selection[0], selection[1]);
+ }
+
+ /*
+ Handle keypresses for a custom undo/redo stack.
+ We need this because the toolbar buttons and indentation helpers mess with the browser's
+ native undo/redo capability.
+ */
+ handleUndo(event) {
+ const content = this.textarea.val();
+ const { selectionStart, selectionEnd } = this.textarea[0];
+ const stack = this.undoStack;
+
+ if (stack.isEmpty()) {
+ // ==== Save initial state in undo history ====
+ stack.save(content);
+ }
+
+ if (keystroke(event, keys.Z_KEY_CODE, 'l')) {
+ // ==== Undo ====
+ event.preventDefault();
+ stack.save(content);
+ if (stack.canUndo()) {
+ this.setState(stack.undo());
+ }
+ } else if (keystroke(event, keys.Z_KEY_CODE, 'ls') || keystroke(event, keys.Y_KEY_CODE, 'l')) {
+ // ==== Redo ====
+ event.preventDefault();
+ if (stack.canRedo()) {
+ this.setState(stack.redo());
+ }
+ } else if (
+ keystroke(event, keys.SPACE_KEY_CODE) ||
+ keystroke(event, keys.ENTER_KEY_CODE) ||
+ selectionStart !== selectionEnd
+ ) {
+ // ==== Save after finishing a word or before deleting a large selection ====
+ stack.save(content);
+ } else if (content === '') {
+ // ==== Save after deleting everything ====
+ stack.save('');
+ } else {
+ // ==== Save after 1 second of inactivity ====
+ stack.scheduleSave(content);
+ }
+ }
+
+ handleIndent(event) {
+ if (keystroke(event, keys.LEFT_BRACKET_KEY_CODE, 'l')) {
+ // ==== Unindent selected lines ====
+ event.preventDefault();
+ this.indentHelper.unindent();
+ } else if (keystroke(event, keys.RIGHT_BRACKET_KEY_CODE, 'l')) {
+ // ==== Indent selected lines ====
+ event.preventDefault();
+ this.indentHelper.indent();
+ } else if (keystroke(event, keys.ENTER_KEY_CODE)) {
+ // ==== Auto-indent new lines ====
+ event.preventDefault();
+ this.indentHelper.newline();
+ } else if (keystroke(event, keys.BACKSPACE_KEY_CODE)) {
+ // ==== Auto-delete indents at the beginning of the line ====
+ this.indentHelper.backspace(event);
+ }
+ }
+
+ handleKeyShortcuts(event) {
+ this.handleIndent(event);
+ this.handleUndo(event);
+ }
+
addEventListeners() {
this.textarea.on('focus', function focusTextArea() {
$(this)
@@ -99,5 +185,6 @@ export default class GLForm {
.closest('.md-area')
.removeClass('is-focused');
});
+ this.textarea.on('keydown', e => this.handleKeyShortcuts(e.originalEvent));
}
}
diff --git a/app/assets/javascripts/helpers/indent_helper.js b/app/assets/javascripts/helpers/indent_helper.js
new file mode 100644
index 00000000000..a8815fac04e
--- /dev/null
+++ b/app/assets/javascripts/helpers/indent_helper.js
@@ -0,0 +1,182 @@
+const INDENT_SEQUENCE = ' ';
+
+function countLeftSpaces(text) {
+ const i = text.split('').findIndex(c => c !== ' ');
+ return i === -1 ? text.length : i;
+}
+
+/**
+ * IndentHelper provides methods that allow manual and smart indentation in
+ * textareas. It supports line indent/unindent, selection indent/unindent,
+ * auto indentation on newlines, and smart deletion of indents with backspace.
+ */
+export default class IndentHelper {
+ /**
+ * Creates a new IndentHelper and binds it to the given `textarea`. You can provide a custom indent sequence in the second parameter, but the `newline` and `backspace` operations may work funny if the indent sequence isn't spaces only.
+ */
+ constructor(textarea, indentSequence = INDENT_SEQUENCE) {
+ this.element = textarea;
+ this.seq = indentSequence;
+ }
+
+ getSelection() {
+ return { start: this.element.selectionStart, end: this.element.selectionEnd };
+ }
+
+ isRangeSelection() {
+ return this.element.selectionStart !== this.element.selectionEnd;
+ }
+
+ /**
+ * Re-implementation of textarea's setRangeText method, because IE/Edge don't support it.
+ *
+ * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea%2Finput-setrangetext
+ */
+ setRangeText(replacement, start, end, selectMode) {
+ // Disable eslint to remain as faithful as possible to the above linked spec
+ /* eslint-disable no-param-reassign, no-case-declarations */
+ const text = this.element.value;
+
+ if (start > end) {
+ throw new RangeError('setRangeText: start index must be less than or equal to end index');
+ }
+
+ // Clamp to [0, len]
+ start = Math.max(0, Math.min(start, text.length));
+ end = Math.max(0, Math.min(end, text.length));
+
+ let selection = { start: this.element.selectionStart, end: this.element.selectionEnd };
+
+ this.element.value = text.slice(0, start) + replacement + text.slice(end);
+
+ const newLength = replacement.length;
+ const newEnd = start + newLength;
+
+ switch (selectMode) {
+ case 'select':
+ selection = { start, newEnd };
+ break;
+ case 'start':
+ selection = { start, end: start };
+ break;
+ case 'end':
+ selection = { start: newEnd, end: newEnd };
+ break;
+ case 'preserve':
+ default:
+ const oldLength = end - start;
+ const delta = newLength - oldLength;
+ if (selection.start > end) {
+ selection.start += delta;
+ } else if (selection.start > start) {
+ selection.start = start;
+ }
+ if (selection.end > end) {
+ selection.end += delta;
+ } else if (selection.end > start) {
+ selection.end = newEnd;
+ }
+ }
+
+ this.element.setSelectionRange(selection.start, selection.end);
+
+ /* eslint-enable no-param-reassign, no-case-declarations */
+ }
+
+ /**
+ * Returns an array of lines in the textarea, with information about their
+ * start/end offsets and whether they are included in the current selection.
+ */
+ splitLines() {
+ const { start, end } = this.getSelection();
+
+ const lines = this.element.value.split('\n');
+ let textStart = 0;
+ const lineObjects = [];
+ lines.forEach(line => {
+ const lineObj = {
+ text: line,
+ start: textStart,
+ end: textStart + line.length,
+ };
+ lineObj.inSelection = lineObj.start <= end && lineObj.end >= start;
+ lineObjects.push(lineObj);
+ textStart += line.length + 1;
+ });
+ return lineObjects;
+ }
+
+ /**
+ * Indents selected lines by one level.
+ */
+ indent() {
+ const { start } = this.getSelection();
+
+ const selectedLines = this.splitLines().filter(line => line.inSelection);
+ if (!this.isRangeSelection() && start === selectedLines[0].start) {
+ // Special case: if cursor is at the beginning of the line, move it one
+ // indent right.
+ const line = selectedLines[0];
+ this.setRangeText(this.seq, line.start, line.start, 'end');
+ } else {
+ selectedLines.reverse();
+ selectedLines.forEach(line => {
+ this.setRangeText(INDENT_SEQUENCE, line.start, line.start, 'preserve');
+ });
+ }
+ }
+
+ /**
+ * Unindents selected lines by one level.
+ */
+ unindent() {
+ const lines = this.splitLines().filter(line => line.inSelection);
+ lines.reverse();
+ lines
+ .filter(line => line.text.startsWith(this.seq))
+ .forEach(line => {
+ this.setRangeText('', line.start, line.start + this.seq.length, 'preserve');
+ });
+ }
+
+ /**
+ * Emulates a newline keypress, automatically indenting the new line.
+ */
+ newline() {
+ const { start, end } = this.getSelection();
+
+ if (this.isRangeSelection()) {
+ // Manually kill the selection before calculating the indent
+ this.setRangeText('', start, end, 'start');
+ }
+
+ // Auto-indent the next line
+ const currentLine = this.splitLines().find(line => line.end >= start);
+ const spaces = countLeftSpaces(currentLine.text);
+ this.setRangeText(`\n${' '.repeat(spaces)}`, start, start, 'end');
+ }
+
+ /**
+ * If the cursor is positioned at the end of a line's leading indents,
+ * emulates a backspace keypress by deleting a single level of indents.
+ * @param event The DOM KeyboardEvent that triggers this action, or null.
+ */
+ backspace(event) {
+ const { start } = this.getSelection();
+
+ // If the cursor is at the end of leading indents, delete an indent.
+ if (!this.isRangeSelection()) {
+ const currentLine = this.splitLines().find(line => line.end >= start);
+ const cursorPosition = start - currentLine.start;
+ if (countLeftSpaces(currentLine.text) === cursorPosition && cursorPosition > 0) {
+ if (event) event.preventDefault();
+
+ let spacesToDelete = cursorPosition % this.seq.length;
+ if (spacesToDelete === 0) {
+ spacesToDelete = this.seq.length;
+ }
+ this.setRangeText('', start - spacesToDelete, start, 'start');
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
index b1d5de8682d..137f8bb18c7 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
@@ -10,7 +10,9 @@ export default {
<template>
<div class="multi-file-commit-panel-success-message" aria-live="assertive">
- <div class="svg-content svg-80"><img :src="committedStateSvgPath" alt="" /></div>
+ <div class="svg-content svg-80">
+ <img :src="committedStateSvgPath" :alt="s__('IDE|Successful commit')" />
+ </div>
<div class="append-right-default prepend-left-default">
<div class="text-content text-center">
<h4>{{ __('All changes are committed') }}</h4>
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 5e90893b684..1a94aee2398 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -203,6 +203,71 @@ export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
// 3) Middle-click or Mouse Wheel Click (e.which is 2)
export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2;
+export const getPlatformLeaderKey = () => {
+ // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
+ if (navigator && navigator.platform && navigator.platform.startsWith('Mac')) {
+ return 'meta';
+ }
+ return 'ctrl';
+};
+
+export const getPlatformLeaderKeyHTML = () => {
+ if (getPlatformLeaderKey() === 'meta') {
+ return '&#8984;';
+ }
+ // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
+ return 'Ctrl';
+};
+
+export const isPlatformLeaderKey = e => {
+ if (getPlatformLeaderKey() === 'meta') {
+ return Boolean(e.metaKey);
+ }
+ return Boolean(e.ctrlKey);
+};
+
+/**
+ * Tests if a KeyboardEvent corresponds exactly to a keystroke.
+ *
+ * This function avoids hacking around an old version of Mousetrap, which we ship at the moment. It should be removed after we upgrade to the newest Mousetrap. See:
+ * - https://gitlab.com/gitlab-org/gitlab-ce/issues/63182
+ * - https://gitlab.com/gitlab-org/gitlab-ce/issues/64246
+ *
+ * @example
+ * // Matches the enter key with exactly zero modifiers
+ * keystroke(event, 13)
+ *
+ * @example
+ * // Matches Control-Shift-Z
+ * keystroke(event, 90, 'cs')
+ *
+ * @param e The KeyboardEvent to test.
+ * @param keyCode The key code of the key to test. Why keycodes? IE/Edge don't support the more convenient `key` and `code` properties.
+ * @param modifiers A string of modifiers keys. Each modifier key is represented by one character. The set of pressed modifier keys must match the given string exactly. Available options are 'a' for Alt/Option, 'c' for Control, 'm' for Meta/Command, 's' for Shift, and 'l' for the leader key (Meta on MacOS and Control otherwise).
+ * @returns {boolean} True if the KeyboardEvent corresponds to the given keystroke.
+ */
+export const keystroke = (e, keyCode, modifiers = '') => {
+ if (!e || !keyCode) {
+ return false;
+ }
+
+ const leader = getPlatformLeaderKey();
+ const mods = modifiers.toLowerCase().replace('l', leader.charAt(0));
+
+ // Match depressed modifier keys
+ if (
+ e.altKey !== mods.includes('a') ||
+ e.ctrlKey !== mods.includes('c') ||
+ e.metaKey !== mods.includes('m') ||
+ e.shiftKey !== mods.includes('s')
+ ) {
+ return false;
+ }
+
+ // Match the depressed key
+ return keyCode === (e.keyCode || e.which);
+};
+
export const contentTop = () => {
const perfBar = $('#js-peek').outerHeight() || 0;
const mrTabsHeight = $('.merge-request-tabs').outerHeight() || 0;
diff --git a/app/assets/javascripts/lib/utils/keycodes.js b/app/assets/javascripts/lib/utils/keycodes.js
index 5e0f9b612a2..e24fcf47d71 100644
--- a/app/assets/javascripts/lib/utils/keycodes.js
+++ b/app/assets/javascripts/lib/utils/keycodes.js
@@ -1,4 +1,10 @@
-export const UP_KEY_CODE = 38;
-export const DOWN_KEY_CODE = 40;
+export const BACKSPACE_KEY_CODE = 8;
export const ENTER_KEY_CODE = 13;
export const ESC_KEY_CODE = 27;
+export const SPACE_KEY_CODE = 32;
+export const UP_KEY_CODE = 38;
+export const DOWN_KEY_CODE = 40;
+export const Y_KEY_CODE = 89;
+export const Z_KEY_CODE = 90;
+export const LEFT_BRACKET_KEY_CODE = 219;
+export const RIGHT_BRACKET_KEY_CODE = 221;
diff --git a/app/assets/javascripts/lib/utils/undo_stack.js b/app/assets/javascripts/lib/utils/undo_stack.js
new file mode 100644
index 00000000000..6cfdc2a0a0f
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/undo_stack.js
@@ -0,0 +1,105 @@
+/**
+ * UndoStack provides a custom implementation of an undo/redo engine. It was originally written for GitLab's Markdown editor (`gl_form.js`), whose rich text editing capabilities broke native browser undo/redo behaviour.
+ *
+ * UndoStack supports predictable undos/redos, debounced saves, maximum history length, and duplicate detection.
+ *
+ * Usage:
+ * - `stack = new UndoStack();`
+ * - Saves a state to the stack with `stack.save(state)`.
+ * - Get the current state with `stack.current()`.
+ * - Revert to the previous state with `stack.undo()`.
+ * - Redo a previous undo with `stack.redo()`;
+ * - Queue a future save with `stack.scheduleSave(state, delay)`. Useful for text editors.
+ * - See the full undo history in `stack.history`.
+ */
+export default class UndoStack {
+ constructor(maxLength = 1000) {
+ this.clear();
+ this.maxLength = maxLength;
+
+ // If you're storing reference-types in the undo stack, you might want to
+ // reassign this property to some deep-equals function.
+ this.comparator = (a, b) => a === b;
+ }
+
+ current() {
+ if (this.cursor === -1) {
+ return undefined;
+ }
+ return this.history[this.cursor];
+ }
+
+ isEmpty() {
+ return this.history.length === 0;
+ }
+
+ clear() {
+ this.clearPending();
+ this.history = [];
+ this.cursor = -1;
+ }
+
+ save(state) {
+ this.clearPending();
+ if (this.comparator(state, this.current())) {
+ // Don't save state if it's the same as the current state
+ return;
+ }
+
+ this.history.length = this.cursor + 1;
+ this.history.push(state);
+ this.cursor += 1;
+
+ if (this.history.length > this.maxLength) {
+ this.history.shift();
+ this.cursor -= 1;
+ }
+ }
+
+ scheduleSave(state, delay = 1000) {
+ this.clearPending();
+ this.pendingState = state;
+ this.timeout = setTimeout(this.saveNow.bind(this), delay);
+ }
+
+ saveNow() {
+ // Persists scheduled saves immediately
+ this.save(this.pendingState);
+ this.clearPending();
+ }
+
+ clearPending() {
+ // Cancels any scheduled saves
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ delete this.timeout;
+ delete this.pendingState;
+ }
+ }
+
+ canUndo() {
+ return this.cursor > 0;
+ }
+
+ undo() {
+ this.clearPending();
+ if (!this.canUndo()) {
+ return undefined;
+ }
+ this.cursor -= 1;
+ return this.history[this.cursor];
+ }
+
+ canRedo() {
+ return this.cursor >= 0 && this.cursor < this.history.length - 1;
+ }
+
+ redo() {
+ this.clearPending();
+ if (!this.canRedo()) {
+ return undefined;
+ }
+ this.cursor += 1;
+ return this.history[this.cursor];
+ }
+}
diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js
index 8001d2dd1da..f091c01fc98 100644
--- a/app/assets/javascripts/pages/projects/clusters/show/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/show/index.js
@@ -1,5 +1,7 @@
import ClustersBundle from '~/clusters/clusters_bundle';
+import initGkeNamespace from '~/projects/gke_cluster_namespace';
document.addEventListener('DOMContentLoaded', () => {
new ClustersBundle(); // eslint-disable-line no-new
+ initGkeNamespace();
});
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index d4bd02c14e9..55c377ebec0 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,4 +1,5 @@
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
+import initGkeNamespace from '~/projects/gke_cluster_namespace';
import PersistentUserCallout from '../../persistent_user_callout';
import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
@@ -16,6 +17,7 @@ document.addEventListener('DOMContentLoaded', () => {
PersistentUserCallout.factory(callout);
initGkeDropdowns();
+ initGkeNamespace();
}
new Project(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
index f05db8376a4..73c9c60765a 100644
--- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
+++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue
@@ -43,13 +43,13 @@ export default {
},
{
metric: 'rugged',
- header: 'Rugged calls',
+ header: s__('PerformanceBar|Rugged calls'),
details: 'details',
keys: ['feature', 'args'],
},
{
metric: 'redis',
- header: 'Redis calls',
+ header: s__('PerformanceBar|Redis calls'),
details: 'details',
keys: ['cmd'],
},
diff --git a/app/assets/javascripts/projects/gke_cluster_namespace/index.js b/app/assets/javascripts/projects/gke_cluster_namespace/index.js
new file mode 100644
index 00000000000..288740203ad
--- /dev/null
+++ b/app/assets/javascripts/projects/gke_cluster_namespace/index.js
@@ -0,0 +1,35 @@
+/**
+ * Disables & hides the namespace inputs when the gitlab-managed checkbox is checked/unchecked.
+ */
+
+const setDisabled = (el, isDisabled) => {
+ if (isDisabled) {
+ el.classList.add('hidden');
+ el.querySelector('input').setAttribute('disabled', true);
+ } else {
+ el.classList.remove('hidden');
+ el.querySelector('input').removeAttribute('disabled');
+ }
+};
+
+const setState = glManagedCheckbox => {
+ const glManaged = document.querySelector('.js-namespace-prefixed');
+ const selfManaged = document.querySelector('.js-namespace');
+
+ if (glManagedCheckbox.checked) {
+ setDisabled(glManaged, false);
+ setDisabled(selfManaged, true);
+ } else {
+ setDisabled(glManaged, true);
+ setDisabled(selfManaged, false);
+ }
+};
+
+const initGkeNamespace = () => {
+ const glManagedCheckbox = document.querySelector('.js-gl-managed');
+
+ setState(glManagedCheckbox); // this is needed in order to set the initial state
+ glManagedCheckbox.addEventListener('change', () => setState(glManagedCheckbox));
+};
+
+export default initGkeNamespace;
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index 8ce5b615795..21c44b59520 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -1,5 +1,6 @@
<script>
import { GlLink } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
export default {
components: {
@@ -22,8 +23,28 @@ export default {
},
},
computed: {
- hasQuickActionsDocsPath() {
- return this.quickActionsDocsPath !== '';
+ toolbarHelpHtml() {
+ const mdLinkStart = `<a href="${this.markdownDocsPath}" target="_blank" rel="noopener noreferrer" tabindex="-1">`;
+ const actionsLinkStart = `<a href="${this.quickActionsDocsPath}" target="_blank" rel="noopener noreferrer" tabindex="-1">`;
+ const linkEnd = '</a>';
+
+ if (this.markdownDocsPath && !this.quickActionsDocsPath) {
+ return sprintf(
+ s__('Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}'),
+ { mdLinkStart, mdLinkEnd: linkEnd },
+ false,
+ );
+ } else if (this.markdownDocsPath && this.quickActionsDocsPath) {
+ return sprintf(
+ s__(
+ 'Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported',
+ ),
+ { mdLinkStart, mdLinkEnd: linkEnd, actionsLinkStart, actionsLinkEnd: linkEnd },
+ false,
+ );
+ }
+
+ return null;
},
},
};
@@ -32,21 +53,7 @@ export default {
<template>
<div class="comment-toolbar clearfix">
<div class="toolbar-text">
- <template v-if="!hasQuickActionsDocsPath && markdownDocsPath">
- <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{
- __('Markdown is supported')
- }}</gl-link>
- </template>
- <template v-if="hasQuickActionsDocsPath && markdownDocsPath">
- <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{
- __('Markdown')
- }}</gl-link>
- and
- <gl-link :href="quickActionsDocsPath" target="_blank" tabindex="-1">{{
- __('quick actions')
- }}</gl-link>
- are supported
- </template>
+ <span v-html="toolbarHelpHtml"></span>
</div>
<span v-if="canAttachFile" class="uploading-container">
<span class="uploading-progress-container hide">
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 61ab0476c42..6b44834cc52 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -441,6 +441,7 @@ img.emoji {
.mw-460 { max-width: 460px; }
.mw-6em { max-width: 6em; }
.mw-70p { max-width: 70%; }
+.mw-90p { max-width: 90%; }
.min-height-0 { min-height: 0; }
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 460d9ea9526..ecd32dcd0ce 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -285,3 +285,19 @@ ul.indent-list {
max-width: 350px;
}
}
+
+.horizontal-list {
+ padding-left: 0;
+ list-style: none;
+
+ > li {
+ float: left;
+ }
+
+ &.list-items-separated {
+ > li:not(:last-child)::after {
+ content: '\00b7';
+ margin: 0 $gl-padding-4;
+ }
+ }
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 0c80a276fce..1d55a073f3b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -421,7 +421,7 @@ class ApplicationController < ActionController::Base
end
def manifest_import_enabled?
- Group.supports_nested_objects? && Gitlab::CurrentSettings.import_sources.include?('manifest')
+ Gitlab::CurrentSettings.import_sources.include?('manifest')
end
def phabricator_import_enabled?
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 4926062f9ca..8c8f0b3a22e 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -55,7 +55,7 @@ module AuthenticatesWithTwoFactor
remember_me(user) if user_params[:remember_me] == '1'
user.save!
- sign_in(user, message: :two_factor_authenticated)
+ sign_in(user, message: :two_factor_authenticated, event: :authentication)
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
@@ -72,7 +72,7 @@ module AuthenticatesWithTwoFactor
session.delete(:challenge)
remember_me(user) if user_params[:remember_me] == '1'
- sign_in(user, message: :two_factor_authenticated)
+ sign_in(user, message: :two_factor_authenticated, event: :authentication)
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F")
diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb
index e9a7d6a3152..d076c62c707 100644
--- a/app/controllers/concerns/group_tree.rb
+++ b/app/controllers/concerns/group_tree.rb
@@ -32,18 +32,14 @@ module GroupTree
def filtered_groups_with_ancestors(groups)
filtered_groups = groups.search(params[:filter]).page(params[:page])
- if Group.supports_nested_objects?
- # We find the ancestors by ID of the search results here.
- # Otherwise the ancestors would also have filters applied,
- # which would cause them not to be preloaded.
- #
- # Pagination needs to be applied before loading the ancestors to
- # make sure ancestors are not cut off by pagination.
- Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id)))
- .base_and_ancestors
- else
- filtered_groups
- end
+ # We find the ancestors by ID of the search results here.
+ # Otherwise the ancestors would also have filters applied,
+ # which would cause them not to be preloaded.
+ #
+ # Pagination needs to be applied before loading the ancestors to
+ # make sure ancestors are not cut off by pagination.
+ Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id)))
+ .base_and_ancestors
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb
index 590eefc6dab..4304b8565ce 100644
--- a/app/controllers/concerns/sessionless_authentication.rb
+++ b/app/controllers/concerns/sessionless_authentication.rb
@@ -13,7 +13,7 @@ module SessionlessAuthentication
end
def sessionless_user?
- current_user && !session.keys.include?('warden.user.user.key')
+ current_user && !session.key?('warden.user.user.key')
end
def sessionless_sign_in(user)
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 2a8dd997d04..b1efa767154 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -139,7 +139,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
if user.two_factor_enabled? && !auth_user.bypass_two_factor?
prompt_for_two_factor(user)
else
- sign_in_and_redirect(user)
+ sign_in_and_redirect(user, event: :authentication)
end
else
fail_login(user)
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 2ef18d900f2..da8a371acaa 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -92,7 +92,10 @@ class Projects::ArtifactsController < Projects::ApplicationController
def build_from_ref
return unless @ref_name
- project.latest_successful_build_for(params[:job], @ref_name)
+ commit = project.commit(@ref_name)
+ return unless commit
+
+ project.latest_successful_build_for_sha(params[:job], commit.id)
end
def artifacts_file
diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb
index 4274c356227..99f4524eec5 100644
--- a/app/controllers/projects/build_artifacts_controller.rb
+++ b/app/controllers/projects/build_artifacts_controller.rb
@@ -51,6 +51,6 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
def job_from_ref
return unless @ref_name
- project.latest_successful_build_for(params[:job], @ref_name)
+ project.latest_successful_build_for_ref(params[:job], @ref_name)
end
end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 3b4215b766e..a51759641e4 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -6,6 +6,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project, except: :create
before_action :assign_archive_vars, only: :archive
+ before_action :assign_append_sha, only: :archive
before_action :authorize_download_code!
before_action :authorize_admin_project!, only: :create
@@ -16,19 +17,64 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
- append_sha = params[:append_sha]
+ set_cache_headers
+ return if archive_not_modified?
- if @ref
- shortname = "#{@project.path}-#{@ref.tr('/', '-')}"
- append_sha = false if @filename == shortname
- end
-
- send_git_archive @repository, ref: @ref, path: params[:path], format: params[:format], append_sha: append_sha
+ send_git_archive @repository, **repo_params
rescue => ex
logger.error("#{self.class.name}: #{ex}")
git_not_found!
end
+ private
+
+ def repo_params
+ @repo_params ||= { ref: @ref, path: params[:path], format: params[:format], append_sha: @append_sha }
+ end
+
+ def set_cache_headers
+ expires_in cache_max_age(archive_metadata['CommitId']), public: project.public?
+ fresh_when(etag: archive_metadata['ArchivePath'])
+ end
+
+ def archive_not_modified?
+ # Check response freshness (Last-Modified and ETag)
+ # against request If-Modified-Since and If-None-Match conditions.
+ request.fresh?(response)
+ end
+
+ def archive_metadata
+ @archive_metadata ||= @repository.archive_metadata(
+ @ref,
+ '', # Where archives are stored isn't really important for ETag purposes
+ repo_params[:format],
+ path: repo_params[:path],
+ append_sha: @append_sha
+ )
+ end
+
+ def cache_max_age(commit_id)
+ if @ref == commit_id
+ # This is a link to an archive by a commit SHA. That means that the archive
+ # is immutable. The only reason to invalidate the cache is if the commit
+ # was deleted or if the user lost access to the repository.
+ Repository::ARCHIVE_CACHE_TIME_IMMUTABLE
+ else
+ # A branch or tag points at this archive. That means that the expected archive
+ # content may change over time.
+ Repository::ARCHIVE_CACHE_TIME
+ end
+ end
+
+ def assign_append_sha
+ @append_sha = params[:append_sha]
+
+ if @ref
+ shortname = "#{@project.path}-#{@ref.tr('/', '-')}"
+ @append_sha = false if @filename == shortname
+ end
+ end
+
def assign_archive_vars
if params[:id]
@ref, @filename = extract_ref(params[:id])
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 7604b31467a..1880bead3ee 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -26,6 +26,17 @@ class SessionsController < Devise::SessionsController
after_action :log_failed_login, if: -> { action_name == 'new' && failed_login? }
helper_method :captcha_enabled?
+ # protect_from_forgery is already prepended in ApplicationController but
+ # authenticate_with_two_factor which signs in the user is prepended before
+ # that here.
+ # We need to make sure CSRF token is verified before authenticating the user
+ # because Devise.clean_up_csrf_token_on_authentication is set to true by
+ # default to avoid CSRF token fixation attacks. Authenticating the user first
+ # would cause the CSRF token to be cleared and then
+ # RequestForgeryProtection#verify_authenticity_token would fail because of
+ # token mismatch.
+ protect_from_forgery with: :exception, prepend: true
+
CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'.freeze
def new
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index ec340f38450..4e489a9c930 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -132,8 +132,6 @@ class GroupDescendantsFinder
end
def subgroups
- return Group.none unless Group.supports_nested_objects?
-
# When filtering subgroups, we want to find all matches within the tree of
# descendants to show to the user
groups = if params[:filter]
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 530aecc2bf9..66b41919914 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -14,10 +14,8 @@ module Types
group.avatar_url(only_path: false)
end
- if ::Group.supports_nested_objects?
- field :parent, GroupType,
- null: true,
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
- end
+ field :parent, GroupType,
+ null: true,
+ resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find }
end
end
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 5906ddabee4..81ff359556d 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -52,7 +52,7 @@ module AvatarsHelper
user: commit_or_event.author,
user_name: commit_or_event.author_name,
user_email: commit_or_event.author_email,
- css_class: 'd-none d-sm-inline'
+ css_class: 'd-none d-sm-inline-block'
}))
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 5aed7e313e6..160f9ac4793 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -127,10 +127,6 @@ module GroupsHelper
groups.to_json
end
- def supports_nested_groups?
- Group.supports_nested_objects?
- end
-
private
def get_group_sidebar_links
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 9a281065b90..e683e2959d1 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -13,6 +13,8 @@ module SubmoduleHelper
end
def submodule_links_for_url(submodule_item_id, url, repository)
+ return [nil, nil] unless url
+
if url == '.' || url == './'
url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 2bfa59774d7..76fa7236ab1 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -9,11 +9,11 @@ module Emails
helper_method :member_source, :member
end
- def member_access_requested_email(member_source_type, member_id, recipient_notification_email)
+ def member_access_requested_email(member_source_type, member_id, recipient_id)
@member_source_type = member_source_type
@member_id = member_id
- mail(to: recipient_notification_email,
+ mail(to: recipient(recipient_id, notification_group),
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end
@@ -21,16 +21,15 @@ module Emails
@member_source_type = member_source_type
@member_id = member_id
- mail(to: member.user.notification_email,
+ mail(to: recipient(member.user, notification_group),
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted"))
end
def member_access_denied_email(member_source_type, source_id, user_id)
@member_source_type = member_source_type
@member_source = member_source_class.find(source_id)
- requester = User.find(user_id)
- mail(to: requester.notification_email,
+ mail(to: recipient(user_id, notification_group),
subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied"))
end
@@ -48,7 +47,7 @@ module Emails
@member_id = member_id
return unless member.created_by
- mail(to: member.created_by.notification_email,
+ mail(to: recipient(member.created_by, notification_group),
subject: subject('Invitation accepted'))
end
@@ -59,7 +58,7 @@ module Emails
@member_source = member_source_class.find(source_id)
@invite_email = invite_email
- mail(to: recipient(created_by_id, member_source_type == 'Project' ? @member_source.group : @member_source),
+ mail(to: recipient(created_by_id, notification_group),
subject: subject('Invitation declined'))
end
@@ -71,6 +70,10 @@ module Emails
@member_source ||= member.source
end
+ def notification_group
+ @member_source_type.casecmp?('project') ? member_source.group : member_source
+ end
+
private
def member_source_class
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 8ef20a03541..5d292094a05 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -71,14 +71,18 @@ class Notify < BaseMailer
address.format
end
- # Look up a User by their ID and return their email address
+ # Look up a User's notification email for a particular context.
+ # Can look up by their ID or can accept a User object.
#
- # recipient_id - User ID
+ # recipient - User object OR a User ID
# notification_group - The parent group of the notification
#
# Returns a String containing the User's email address.
- def recipient(recipient_id, notification_group = nil)
- User.find(recipient_id).notification_email_for(notification_group)
+ def recipient(recipient, notification_group = nil)
+ user = recipient if recipient.is_a?(User)
+ user ||= User.find(recipient)
+
+ user.notification_email_for(notification_group)
end
# Formats arguments into a String suitable for use as an email subject
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 80e0a17c312..b3fab930922 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -105,11 +105,11 @@ class NotifyPreview < ActionMailer::Preview
end
def member_access_granted_email
- Notify.member_access_granted_email('project', user.id).message
+ Notify.member_access_granted_email(member.source_type, member.id).message
end
def member_access_requested_email
- Notify.member_access_requested_email('group', user.id, 'some@example.com').message
+ Notify.member_access_requested_email('group', user.id, user.id).message
end
def member_invite_accepted_email
@@ -183,6 +183,10 @@ class NotifyPreview < ActionMailer::Preview
@user ||= User.last
end
+ def member
+ @member ||= Member.last
+ end
+
def create_note(params)
Notes::CreateService.new(project, user, params).execute
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index c2eb51ba100..1c76f401690 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -229,10 +229,12 @@ module Ci
#
# ref - The name (or names) of the branch(es)/tag(s) to limit the list of
# pipelines to.
+ # sha - The commit SHA (or mutliple SHAs) to limit the list of pipelines to.
# limit - This limits a backlog search, default to 100.
- def self.newest_first(ref: nil, limit: 100)
+ def self.newest_first(ref: nil, sha: nil, limit: 100)
relation = order(id: :desc)
relation = relation.where(ref: ref) if ref
+ relation = relation.where(sha: sha) if sha
if limit
ids = relation.limit(limit).select(:id)
@@ -246,10 +248,14 @@ module Ci
newest_first(ref: ref).pluck(:status).first
end
- def self.latest_successful_for(ref)
+ def self.latest_successful_for_ref(ref)
newest_first(ref: ref).success.take
end
+ def self.latest_successful_for_sha(sha)
+ newest_first(sha: sha).success.take
+ end
+
def self.latest_successful_for_refs(refs)
relation = newest_first(ref: refs).success
diff --git a/app/models/commit.rb b/app/models/commit.rb
index be37fa2e76f..0889ce7e287 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -346,7 +346,7 @@ class Commit
if commits_in_merge_request.present?
message_body << ""
- commits_in_merge_request.reverse.each do |commit_in_merge|
+ commits_in_merge_request.reverse_each do |commit_in_merge|
message_body << "#{commit_in_merge.short_id} #{commit_in_merge.title}"
end
end
diff --git a/app/models/concerns/descendant.rb b/app/models/concerns/descendant.rb
deleted file mode 100644
index 4c436522122..00000000000
--- a/app/models/concerns/descendant.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Descendant
- extend ActiveSupport::Concern
-
- class_methods do
- def supports_nested_objects?
- Gitlab::Database.postgresql?
- end
- end
-end
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index af387c99f3d..0648b4a78e1 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -47,7 +47,7 @@ module ProtectedRef
def access_levels_for_ref(ref, action:, protected_refs: nil)
self.matching(ref, protected_refs: protected_refs)
- .map(&:"#{action}_access_levels").flatten
+ .flat_map(&:"#{action}_access_levels")
end
# Returns all protected refs that match the given ref name.
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index e4fe46d722a..9cd7b8d6258 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -1,5 +1,26 @@
# frozen_string_literal: true
+# This module makes it possible to handle items as a list, where the order of items can be easily altered
+# Requirements:
+#
+# - Only works for ActiveRecord models
+# - relative_position integer field must present on the model
+# - This module uses GROUP BY: the model should have a parent relation, example: project -> issues, project is the parent relation (issues table has a parent_id column)
+#
+# Setup like this in the body of your class:
+#
+# include RelativePositioning
+#
+# # base query used for the position calculation
+# def self.relative_positioning_query_base(issue)
+# where(deleted: false)
+# end
+#
+# # column that should be used in GROUP BY
+# def self.relative_positioning_parent_column
+# :project_id
+# end
+#
module RelativePositioning
extend ActiveSupport::Concern
@@ -93,7 +114,7 @@ module RelativePositioning
return move_after(before) unless after
return move_before(after) unless before
- # If there is no place to insert an issue we need to create one by moving the before issue closer
+ # If there is no place to insert an item we need to create one by moving the before item closer
# to its predecessor. This process will recursively move all the predecessors until we have a place
if (after.relative_position - before.relative_position) < 2
before.move_before
@@ -108,11 +129,11 @@ module RelativePositioning
pos_after = before.next_relative_position
if before.shift_after?
- issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_after)
- issue_to_move.move_after
- @positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ item_to_move = self.class.relative_positioning_query_base(self).find_by!(relative_position: pos_after)
+ item_to_move.move_after
+ @positionable_neighbours = [item_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
- pos_after = issue_to_move.relative_position
+ pos_after = item_to_move.relative_position
end
self.relative_position = self.class.position_between(pos_before, pos_after)
@@ -123,11 +144,11 @@ module RelativePositioning
pos_before = after.prev_relative_position
if after.shift_before?
- issue_to_move = self.class.in_parents(parent_ids).find_by!(relative_position: pos_before)
- issue_to_move.move_before
- @positionable_neighbours = [issue_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ item_to_move = self.class.relative_positioning_query_base(self).find_by!(relative_position: pos_before)
+ item_to_move.move_before
+ @positionable_neighbours = [item_to_move] # rubocop:disable Gitlab/ModuleWithInstanceVariables
- pos_before = issue_to_move.relative_position
+ pos_before = item_to_move.relative_position
end
self.relative_position = self.class.position_between(pos_before, pos_after)
@@ -141,13 +162,13 @@ module RelativePositioning
self.relative_position = self.class.position_between(min_relative_position || START_POSITION, MIN_POSITION)
end
- # Indicates if there is an issue that should be shifted to free the place
+ # Indicates if there is an item that should be shifted to free the place
def shift_after?
next_pos = next_relative_position
next_pos && (next_pos - relative_position) == 1
end
- # Indicates if there is an issue that should be shifted to free the place
+ # Indicates if there is an item that should be shifted to free the place
def shift_before?
prev_pos = prev_relative_position
prev_pos && (relative_position - prev_pos) == 1
@@ -159,7 +180,7 @@ module RelativePositioning
def save_positionable_neighbours
return unless @positionable_neighbours
- status = @positionable_neighbours.all? { |issue| issue.save(touch: false) }
+ status = @positionable_neighbours.all? { |item| item.save(touch: false) }
@positionable_neighbours = nil
status
@@ -170,16 +191,15 @@ module RelativePositioning
# When calculating across projects, this is much more efficient than
# MAX(relative_position) without the GROUP BY, due to index usage:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/54276#note_119340977
- relation = self.class
- .in_parents(parent_ids)
+ relation = self.class.relative_positioning_query_base(self)
.order(Gitlab::Database.nulls_last_order('position', 'DESC'))
+ .group(self.class.relative_positioning_parent_column)
.limit(1)
- .group(self.class.parent_column)
relation = yield relation if block_given?
relation
- .pluck(self.class.parent_column, Arel.sql("#{calculation}(relative_position) AS position"))
+ .pluck(self.class.relative_positioning_parent_column, Arel.sql("#{calculation}(relative_position) AS position"))
.first&.
last
end
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 39e12ac2b06..facd81dde80 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -86,4 +86,9 @@ class ContainerRepository < ApplicationRecord
def self.build_root_repository(project)
self.new(project: project, name: '')
end
+
+ def self.find_by_path!(path)
+ self.find_by!(project: path.repository_project,
+ name: path.repository_name)
+ end
end
diff --git a/app/models/deployment_metrics.rb b/app/models/deployment_metrics.rb
index cfe762ca25e..2056c8bc59c 100644
--- a/app/models/deployment_metrics.rb
+++ b/app/models/deployment_metrics.rb
@@ -44,18 +44,7 @@ class DeploymentMetrics
end
end
- # TODO remove fallback case to deployment_platform_cluster.
- # Otherwise we will continue to pay the performance penalty described in
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/63475
- #
- # Removal issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64105
def cluster_prometheus
- cluster_with_fallback = cluster || deployment_platform_cluster
-
- cluster_with_fallback.application_prometheus if cluster_with_fallback&.application_prometheus_available?
- end
-
- def deployment_platform_cluster
- deployment.environment.deployment_platform&.cluster
+ cluster.application_prometheus if cluster&.application_prometheus_available?
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 26ce2957e9b..74eb556b1b5 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -10,7 +10,6 @@ class Group < Namespace
include Referable
include SelectForProjectAuthorization
include LoadedInGroupList
- include Descendant
include GroupDescendant
include TokenAuthenticatable
include WithUploads
@@ -388,7 +387,7 @@ class Group < Namespace
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
- list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten
+ list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
end
def group_member(user)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 8c5dd5e382e..164858dc432 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -91,11 +91,11 @@ class Issue < ApplicationRecord
end
end
- class << self
- alias_method :in_parents, :in_projects
+ def self.relative_positioning_query_base(issue)
+ in_projects(issue.parent_ids)
end
- def self.parent_column
+ def self.relative_positioning_parent_column
:project_id
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b8d7348268a..058350b16ce 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -332,8 +332,6 @@ class Namespace < ApplicationRecord
end
def force_share_with_group_lock_on_descendants
- return unless Group.supports_nested_objects?
-
# We can't use `descendants.update_all` since Rails will throw away the WITH
# RECURSIVE statement. We also can't use WHERE EXISTS since we can't use
# different table aliases, hence we're just using WHERE IN. Since we have a
diff --git a/app/models/note.rb b/app/models/note.rb
index 5c31cff9816..3f182c1f099 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -292,7 +292,7 @@ class Note < ApplicationRecord
end
def special_role=(role)
- raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.values.include?(role)
+ raise "Role is undefined, #{role} not found in #{SpecialRole.values}" unless SpecialRole.value?(role)
@special_role = role
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 0020e423628..cca7da8c49a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -719,16 +719,27 @@ class Project < ApplicationRecord
repository.commits_by(oids: oids)
end
- # ref can't be HEAD, can only be branch/tag name or SHA
- def latest_successful_build_for(job_name, ref = default_branch)
- latest_pipeline = ci_pipelines.latest_successful_for(ref)
+ # ref can't be HEAD, can only be branch/tag name
+ def latest_successful_build_for_ref(job_name, ref = default_branch)
+ return unless ref
+
+ latest_pipeline = ci_pipelines.latest_successful_for_ref(ref)
+ return unless latest_pipeline
+
+ latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name)
+ end
+
+ def latest_successful_build_for_sha(job_name, sha)
+ return unless sha
+
+ latest_pipeline = ci_pipelines.latest_successful_for_sha(sha)
return unless latest_pipeline
latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name)
end
- def latest_successful_build_for!(job_name, ref = default_branch)
- latest_successful_build_for(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
+ def latest_successful_build_for_ref!(job_name, ref = default_branch)
+ latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
end
def merge_base_commit(first_commit_id, second_commit_id)
@@ -1503,12 +1514,12 @@ class Project < ApplicationRecord
end
@latest_successful_pipeline_for_default_branch =
- ci_pipelines.latest_successful_for(default_branch)
+ ci_pipelines.latest_successful_for_ref(default_branch)
end
def latest_successful_pipeline_for(ref = nil)
if ref && ref != default_branch
- ci_pipelines.latest_successful_for(ref)
+ ci_pipelines.latest_successful_for_ref(ref)
else
latest_successful_pipeline_for_default_branch
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 7ab79242cc3..d08fcd8954d 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -31,7 +31,7 @@ class JiraService < IssueTrackerService
# {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1
def self.reference_pattern(only_long: true)
- @reference_pattern ||= /(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)/
+ @reference_pattern ||= /(?<issue>\b#{Gitlab::Regex.jira_issue_key_regex})/
end
def initialize_properties
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 187382ad182..a89f573e3d6 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -7,6 +7,9 @@ class Repository
REF_KEEP_AROUND = 'keep-around'.freeze
REF_ENVIRONMENTS = 'environments'.freeze
+ ARCHIVE_CACHE_TIME = 60 # Cache archives referred to by a (mutable) ref for 1 minute
+ ARCHIVE_CACHE_TIME_IMMUTABLE = 3600 # Cache archives referred to by an immutable reference for 1 hour
+
RESERVED_REFS_NAMES = %W[
heads
tags
diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb
index f1326f4c8cb..b236250c24e 100644
--- a/app/models/user_preference.rb
+++ b/app/models/user_preference.rb
@@ -26,7 +26,7 @@ class UserPreference < ApplicationRecord
def set_notes_filter(filter_id, issuable)
# No need to update the column if the value is already set.
- if filter_id && NOTES_FILTERS.values.include?(filter_id)
+ if filter_id && NOTES_FILTERS.value?(filter_id)
field = notes_filter_field_for(issuable)
self[field] = filter_id
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 0add8bfad31..84b1873c05d 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -16,8 +16,6 @@ class GroupPolicy < BasePolicy
condition(:maintainer) { access_level >= GroupMember::MAINTAINER }
condition(:reporter) { access_level >= GroupMember::REPORTER }
- condition(:nested_groups_supported, scope: :global) { Group.supports_nested_objects? }
-
condition(:has_parent, scope: :subject) { @subject.has_parent? }
condition(:share_with_group_locked, scope: :subject) { @subject.share_with_group_lock? }
condition(:parent_share_with_group_locked, scope: :subject) { @subject.parent&.share_with_group_lock? }
@@ -108,8 +106,8 @@ class GroupPolicy < BasePolicy
enable :read_nested_project_resources
end
- rule { owner & nested_groups_supported }.enable :create_subgroup
- rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup
+ rule { owner }.enable :create_subgroup
+ rule { maintainer & maintainer_can_create_group }.enable :create_subgroup
rule { public_group | logged_in_viewable }.enable :view_globally
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 029dd3d0684..0b0454c5282 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -59,14 +59,14 @@ class StageEntity < Grape::Entity
end
def latest_statuses
- HasStatus::ORDERED_STATUSES.map do |ordered_status|
+ HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_statuses.fetch(ordered_status, [])
- end.flatten
+ end
end
def retried_statuses
- HasStatus::ORDERED_STATUSES.map do |ordered_status|
+ HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_retried_statuses.fetch(ordered_status, [])
- end.flatten
+ end
end
end
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index aaf56048b5c..207cc5017d0 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -10,13 +10,13 @@ module Ci
update_retried
new_builds =
- stage_indexes_of_created_processables.map do |index|
+ stage_indexes_of_created_processables.flat_map do |index|
process_stage(index)
end
@pipeline.update_status
- new_builds.flatten.any?
+ new_builds.any?
end
private
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index 01bd685712b..a51ac9aa593 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -18,10 +18,6 @@ module Groups
return namespace
end
- if group_path.include?('/') && !Group.supports_nested_objects?
- raise 'Nested groups are not supported on MySQL'
- end
-
create_group_path
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index 98e7c311572..fe7e07ef9f0 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -43,7 +43,6 @@ module Groups
def ensure_allowed_transfer
raise_transfer_error(:group_is_already_root) if group_is_already_root?
- raise_transfer_error(:database_not_supported) unless Group.supports_nested_objects?
raise_transfer_error(:same_parent_as_current) if same_parent?
raise_transfer_error(:invalid_policies) unless valid_policies?
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index c8d5e563cd8..0164760920f 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -31,7 +31,7 @@ module Members
return unless member.is_a?(GroupMember) && member.user && member.group
delete_project_members(member)
- delete_subgroup_members(member) if Group.supports_nested_objects?
+ delete_subgroup_members(member)
end
def delete_project_members(member)
diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb
index 6d70b5106c7..b210004e6e1 100644
--- a/app/services/merge_requests/push_options_handler_service.rb
+++ b/app/services/merge_requests/push_options_handler_service.rb
@@ -118,7 +118,14 @@ module MergeRequests
end
def base_params
- params = {}
+ params = {
+ title: push_options[:title],
+ description: push_options[:description],
+ target_branch: push_options[:target],
+ force_remove_source_branch: push_options[:remove_source_branch]
+ }
+
+ params.compact!
if push_options.key?(:merge_when_pipeline_succeeds)
params.merge!(
@@ -127,14 +134,6 @@ module MergeRequests
)
end
- if push_options.key?(:remove_source_branch)
- params[:force_remove_source_branch] = push_options[:remove_source_branch]
- end
-
- if push_options.key?(:target)
- params[:target_branch] = push_options[:target]
- end
-
params
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index a55771ed538..21fab22e0d4 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -595,7 +595,7 @@ class NotificationService
end
def deliver_access_request_email(recipient, member)
- mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
+ mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.id).deliver_later
end
def fallback_to_group_owners_maintainers?(recipients, member)
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 4a26d2be2af..ae67b4f5256 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -102,13 +102,7 @@ module Users
end
def fresh_authorizations
- klass = if Group.supports_nested_objects?
- Gitlab::ProjectAuthorizations::WithNestedGroups
- else
- Gitlab::ProjectAuthorizations::WithoutNestedGroups
- end
-
- klass.new(user).calculate
+ Gitlab::ProjectAuthorizations.new(user).calculate
end
end
end
diff --git a/app/views/clusters/clusters/_namespace.html.haml b/app/views/clusters/clusters/_namespace.html.haml
new file mode 100644
index 00000000000..0c64819ad62
--- /dev/null
+++ b/app/views/clusters/clusters/_namespace.html.haml
@@ -0,0 +1,14 @@
+- managed_namespace_help_text = s_('ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path.')
+- non_managed_namespace_help_text = s_('ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals.')
+- managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/index.md',
+ anchor: 'gitlab-managed-clusters'), target: '_blank'
+
+.js-namespace-prefixed
+ = platform_field.text_field :namespace,
+ label: s_('ClusterIntegration|Project namespace prefix (optional, unique)'), label_class: 'label-bold',
+ help: '%{help_text} %{help_link}'.html_safe % { help_text: managed_namespace_help_text, help_link: managed_namespace_help_link }
+.js-namespace.hidden
+ = platform_field.text_field :namespace,
+ label: s_('ClusterIntegration|Project namespace (optional, unique)'), label_class: 'label-bold',
+ help: '%{help_text}'.html_safe % { help_text: non_managed_namespace_help_text },
+ disabled: true
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index f2fc5ac93fb..1d212553c3b 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -34,10 +34,6 @@
autocomplete: 'off', label_class: 'label-bold',
help: '%{help_text} %{help_link}'.html_safe % { help_text: token_help_text, help_link: more_info_link }
- - if @user_cluster.allow_user_defined_namespace?
- = platform_kubernetes_field.text_field :namespace,
- label: s_('ClusterIntegration|Project namespace (optional, unique)'), label_class: 'label-bold'
-
= platform_kubernetes_field.form_group :authorization_type,
{ help: '%{help_text} %{help_link}'.html_safe % { help_text: rbac_help_text, help_link: rbac_help_link } } do
= platform_kubernetes_field.check_box :authorization_type,
@@ -46,10 +42,15 @@
.form-group
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
+ class: 'js-gl-managed',
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
+ = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
+ - if @user_cluster.allow_user_defined_namespace?
+ = render('clusters/clusters/namespace', platform_field: platform_kubernetes_field)
+
.form-group
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
diff --git a/app/views/clusters/platforms/kubernetes/_form.html.haml b/app/views/clusters/platforms/kubernetes/_form.html.haml
index f2e44462226..e50c573bd90 100644
--- a/app/views/clusters/platforms/kubernetes/_form.html.haml
+++ b/app/views/clusters/platforms/kubernetes/_form.html.haml
@@ -36,10 +36,6 @@
label: s_('ClusterIntegration|Service Token'), label_class: 'label-bold',
input_group_class: 'gl-field-error-anchor', append: show_token_btn + copy_token_btn
- - if cluster.allow_user_defined_namespace?
- = platform_field.text_field :namespace, label: s_('ClusterIntegration|Project namespace (optional, unique)'),
- label_class: 'label-bold'
-
= platform_field.form_group :authorization_type do
= platform_field.check_box :authorization_type, { disabled: true, label: s_('ClusterIntegration|RBAC-enabled cluster'),
label_class: 'label-bold', inline: true }, 'rbac', 'abac'
@@ -49,10 +45,14 @@
.form-group
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
+ class: 'js-gl-managed',
label_class: 'label-bold' }
.form-text.text-muted
= s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank'
+ - if cluster.allow_user_defined_namespace?
+ = render('clusters/clusters/namespace', platform_field: platform_field)
+
.form-group
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
diff --git a/app/views/groups/settings/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml
index 5d211d0e186..d1eb6478997 100644
--- a/app/views/groups/settings/_advanced.html.haml
+++ b/app/views/groups/settings/_advanced.html.haml
@@ -23,20 +23,19 @@
= f.submit 'Change group path', class: 'btn btn-warning'
-- if supports_nested_groups?
- .sub-section
- %h4.warning-title Transfer group
- = form_for @group, url: transfer_group_path(@group), method: :put do |f|
- .form-group
- = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } })
- = hidden_field_tag 'new_parent_group_id'
+.sub-section
+ %h4.warning-title Transfer group
+ = form_for @group, url: transfer_group_path(@group), method: :put do |f|
+ .form-group
+ = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } })
+ = hidden_field_tag 'new_parent_group_id'
- %ul
- %li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}.
- %li You can only transfer the group to a group you manage.
- %li You will need to update your local repositories to point to the new location.
- %li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
- = f.submit 'Transfer group', class: 'btn btn-warning'
+ %ul
+ %li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}.
+ %li You can only transfer the group to a group you manage.
+ %li You will need to update your local repositories to point to the new location.
+ %li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
+ = f.submit 'Transfer group', class: 'btn btn-warning'
.sub-section
%h4.danger-title= _('Remove group')
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index d1634eb62c0..92dd558885a 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -9,7 +9,7 @@
= @project.name
%ul.sidebar-top-level-items
= nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
- = link_to project_path(@project), class: 'shortcuts-project qa-link-project' do
+ = link_to project_path(@project), class: 'shortcuts-project', data: { qa_selector: 'project_link' } do
.nav-icon-container
= sprite_icon('home')
%span.nav-item-name
@@ -26,7 +26,7 @@
%span= _('Details')
= nav_link(path: 'projects#activity') do
- = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity qa-activity-link' do
+ = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity', data: { qa_selector: 'activity_link' } do
%span= _('Activity')
- if project_nav_tab?(:releases)
@@ -146,7 +146,7 @@
- if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
- = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests qa-merge-requests-link' do
+ = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests', data: { qa_selector: 'merge_requests_link' } do
.nav-icon-container
= sprite_icon('git-merge')
%span.nav-item-name#js-onboarding-mr-link
@@ -277,7 +277,7 @@
- if project_nav_tab? :wiki
- wiki_url = project_wiki_path(@project, :home)
= nav_link(controller: :wikis) do
- = link_to wiki_url, class: 'shortcuts-wiki qa-wiki-link' do
+ = link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do
.nav-icon-container
= sprite_icon('book')
%span.nav-item-name
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index fe74dc122c3..04b77fb987a 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -8,7 +8,7 @@
.card.auto-devops-card
.card-body
.form-check
- = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: auto_devops_enabled
+ = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: auto_devops_enabled, data: { qa_selector: 'enable_autodevops_checkbox' }
= form.label :enabled, class: 'form-check-label' do
%strong= s_('CICD|Default to Auto DevOps pipeline')
- if auto_devops_enabled
@@ -42,4 +42,4 @@
= s_('CICD|Automatic deployment to staging, manual deployment to production')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank'
- = f.submit _('Save changes'), class: "btn btn-success prepend-top-15"
+ = f.submit _('Save changes'), class: "btn btn-success prepend-top-15", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 5e3e1076c2c..293f0a241eb 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -16,7 +16,7 @@
.settings-content
= render 'form'
-%section.qa-autodevops-settings.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) }
+%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'autodevops_settings_content' } }
.settings-header
%h4
= s_('CICD|Auto DevOps')
@@ -30,7 +30,7 @@
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
-%section.qa-runners-settings.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded) }
+%section.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded), data: { qa_selector: 'runners_settings_content' } }
.settings-header
%h4
= _("Runners")
@@ -41,7 +41,7 @@
.settings-content
= render 'projects/runners/index'
-%section.qa-variables-settings.settings.no-animate{ class: ('expanded' if expanded) }
+%section.settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'variables_settings_content' } }
.settings-header
= render 'ci/variables/header', expanded: expanded
.settings-content
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 02f6ef02843..78cce58938e 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -4,7 +4,7 @@
- page_title @tag.name, s_('TagsPage|Tags')
%div{ class: container_class }
- .top-area.multi-line
+ .top-area.multi-line.flex-wrap
.nav-text
.title
%span.item-title.ref-name
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index fae7d6526e8..72ede50dd8c 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -1,14 +1,13 @@
- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
.comment-toolbar.clearfix
.toolbar-text
- = link_to _('Markdown'), help_page_path('user/markdown'), target: '_blank', tabindex: -1
+ - md_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" tabindex="-1">'.html_safe % { url: help_page_path('user/markdown') }
+ - actions_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" tabindex="-1">'.html_safe % { url: help_page_path('user/project/quick_actions') }
+ - link_end = '</a>'.html_safe
- if supports_quick_actions
- and
- = link_to _('quick actions'), help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
- are
+ = s_('Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported').html_safe % { mdLinkStart: md_link_start, mdLinkEnd: link_end, actionsLinkStart: actions_link_start, actionsLinkEnd: link_end }
- else
- is
- supported
+ = s_('Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}').html_safe % { mdLinkStart: md_link_start, mdLinkEnd: link_end }
%span.uploading-container
%span.uploading-progress-container.hide
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index c4d1bdad2c4..f40a9cffb29 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -89,4 +89,6 @@
%span.icon-wrapper.pipeline-status
= render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), type: 'commit', tooltip_placement: 'top', path: pipeline_path
.updated-note
- %span #{_('Updated')} #{updated_tooltip}
+ %span
+ = _('Updated')
+ = updated_tooltip
diff --git a/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml b/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml
new file mode 100644
index 00000000000..38cfa0f273e
--- /dev/null
+++ b/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml
@@ -0,0 +1,5 @@
+---
+title: 'fix: updates to include units for the y axis label'
+merge_request: 30330
+author:
+type: fixed
diff --git a/changelogs/unreleased/63568-access-email-notifications-custom-email.yml b/changelogs/unreleased/63568-access-email-notifications-custom-email.yml
new file mode 100644
index 00000000000..ece6442d7cf
--- /dev/null
+++ b/changelogs/unreleased/63568-access-email-notifications-custom-email.yml
@@ -0,0 +1,5 @@
+---
+title: Respect group notification email when sending group access notifications
+merge_request: 31089
+author:
+type: fixed
diff --git a/changelogs/unreleased/64257-warden_set_user_fix.yml b/changelogs/unreleased/64257-warden_set_user_fix.yml
new file mode 100644
index 00000000000..7b6818876fb
--- /dev/null
+++ b/changelogs/unreleased/64257-warden_set_user_fix.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure Warden triggers after_authentication callback
+merge_request: 31138
+author:
+type: fixed
diff --git a/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml b/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml
new file mode 100644
index 00000000000..fb0f4cedc62
--- /dev/null
+++ b/changelogs/unreleased/64746-Commit-authors-avatar-sretched-in-commit-view-if-no-image-is-loaded.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed distorted avatars when resource not reachable
+merge_request: 30904
+author: Marc Schwede
+type: other
diff --git a/changelogs/unreleased/64763-fix-tags-page-layout.yml b/changelogs/unreleased/64763-fix-tags-page-layout.yml
new file mode 100644
index 00000000000..db6b1f31506
--- /dev/null
+++ b/changelogs/unreleased/64763-fix-tags-page-layout.yml
@@ -0,0 +1,5 @@
+---
+title: Fix tag page layout
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml b/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml
new file mode 100644
index 00000000000..f80111f0bd9
--- /dev/null
+++ b/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pid discovery for Unicorn processes in `PidProvider`
+merge_request: 31056
+author:
+type: fixed
diff --git a/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml b/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml
new file mode 100644
index 00000000000..dd74b8443bc
--- /dev/null
+++ b/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml
@@ -0,0 +1,5 @@
+---
+title: Fix incorrect use of message interpolation
+merge_request: 31121
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-caching-to-archive-endpoint.yml b/changelogs/unreleased/add-caching-to-archive-endpoint.yml
new file mode 100644
index 00000000000..770ec16e804
--- /dev/null
+++ b/changelogs/unreleased/add-caching-to-archive-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Return an ETag header for the archive endpoint
+merge_request: 30581
+author:
+type: added
diff --git a/changelogs/unreleased/delete-designs-v2.yml b/changelogs/unreleased/delete-designs-v2.yml
new file mode 100644
index 00000000000..a678e4f93b9
--- /dev/null
+++ b/changelogs/unreleased/delete-designs-v2.yml
@@ -0,0 +1,4 @@
+---
+title: Adds event enum column to DesignsVersions join table
+merge_request: 30745
+type: added
diff --git a/changelogs/unreleased/dm-submodule-links-nil.yml b/changelogs/unreleased/dm-submodule-links-nil.yml
new file mode 100644
index 00000000000..c09ca41d01d
--- /dev/null
+++ b/changelogs/unreleased/dm-submodule-links-nil.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error rendering submodules in MR diffs when there is no .gitmodules
+merge_request: 31162
+author:
+type: fixed
diff --git a/changelogs/unreleased/extract_auto_deploy_into_base_image.yml b/changelogs/unreleased/extract_auto_deploy_into_base_image.yml
new file mode 100644
index 00000000000..ff0d1f3bd71
--- /dev/null
+++ b/changelogs/unreleased/extract_auto_deploy_into_base_image.yml
@@ -0,0 +1,5 @@
+---
+title: Extract Auto DevOps deploy functions into a base image
+merge_request: 30404
+author:
+type: changed
diff --git a/changelogs/unreleased/je-separate-namespace-fe.yml b/changelogs/unreleased/je-separate-namespace-fe.yml
new file mode 100644
index 00000000000..fb6be071eb6
--- /dev/null
+++ b/changelogs/unreleased/je-separate-namespace-fe.yml
@@ -0,0 +1,5 @@
+---
+title: Update namespace label for GitLab-managed clusters
+merge_request: 30935
+author:
+type: added
diff --git a/changelogs/unreleased/label-descr-push-opts.yml b/changelogs/unreleased/label-descr-push-opts.yml
new file mode 100644
index 00000000000..9b01cfdaed2
--- /dev/null
+++ b/changelogs/unreleased/label-descr-push-opts.yml
@@ -0,0 +1,5 @@
+---
+title: Support setting of merge request title and description using git push options
+merge_request: 31068
+author:
+type: added
diff --git a/changelogs/unreleased/mh-editor-indents.yml b/changelogs/unreleased/mh-editor-indents.yml
new file mode 100644
index 00000000000..a282c0f505d
--- /dev/null
+++ b/changelogs/unreleased/mh-editor-indents.yml
@@ -0,0 +1,5 @@
+---
+title: Markdown editors now have indentation shortcuts and auto-indentation
+merge_request: 28914
+author:
+type: added
diff --git a/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml b/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml
new file mode 100644
index 00000000000..d32cbd1d8e0
--- /dev/null
+++ b/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml
@@ -0,0 +1,6 @@
+---
+title: Remove incorrect fallback when determining which cluster to use when retrieving
+ MR performance metrics
+merge_request: 31126
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-support-docker-oci-images.yml b/changelogs/unreleased/sh-support-docker-oci-images.yml
new file mode 100644
index 00000000000..2dcf980fa50
--- /dev/null
+++ b/changelogs/unreleased/sh-support-docker-oci-images.yml
@@ -0,0 +1,5 @@
+---
+title: Support Docker OCI images
+merge_request: 31127
+author:
+type: fixed
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 0e78980350f..dd53127ac2c 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -400,6 +400,15 @@ production: &base
# path: shared/registry
# issuer: gitlab-issuer
+ # Add notification settings if you plan to use Geo Replication for the registry
+ # notifications:
+ # - name: geo_event
+ # url: https://example.com/api/v4/container_registry_event/events
+ # timeout: 2s
+ # threshold: 5
+ # backoff: 1s
+ # headers:
+ # Authorization: secret_phrase
## Error Reporting and Logging with Sentry
sentry:
diff --git a/config/initializers/0_license.rb b/config/initializers/0_license.rb
index f750022dfdf..19c71c34904 100644
--- a/config/initializers/0_license.rb
+++ b/config/initializers/0_license.rb
@@ -10,7 +10,7 @@ Gitlab.ee do
end
# Needed to run migration
- if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.data_source_exists?('licenses')
+ if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('licenses')
message = LicenseHelper.license_message(signed_in: true, is_admin: true, in_html: false)
if ::License.block_changes? && message.present?
warn "WARNING: #{message}"
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 494c4dd1f93..32fec7c3d22 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -259,6 +259,7 @@ Settings.registry['key'] ||= nil
Settings.registry['issuer'] ||= nil
Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
Settings.registry['path'] = Settings.absolute(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry'))
+Settings.registry['notifications'] ||= []
#
# Error Reporting and Logging with Sentry
diff --git a/config/initializers/active_record_migration.rb b/config/initializers/active_record_migration.rb
deleted file mode 100644
index 04c06be7834..00000000000
--- a/config/initializers/active_record_migration.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'active_record/migration'
-
-module ActiveRecord
- class Migration
- # data_source_exists? is not available in 4.2.10, table_exists deprecated in 5.0
- def table_exists?(table_name)
- ActiveRecord::Base.connection.data_source_exists?(table_name)
- end
- end
-end
diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb
index 029c0ff4277..a49bcbe1f96 100644
--- a/config/initializers/load_balancing.rb
+++ b/config/initializers/load_balancing.rb
@@ -3,7 +3,7 @@
# We need to run this initializer after migrations are done so it doesn't fail on CI
Gitlab.ee do
- if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.data_source_exists?('licenses')
+ if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('licenses')
if Gitlab::Database::LoadBalancing.enable?
Gitlab::Database.disable_prepared_statements
diff --git a/config/prometheus/cluster_metrics.yml b/config/prometheus/cluster_metrics.yml
index 3df76b0974f..f2a41e4c337 100644
--- a/config/prometheus/cluster_metrics.yml
+++ b/config/prometheus/cluster_metrics.yml
@@ -2,12 +2,12 @@
priority: 1
metrics:
- title: "CPU Usage"
- y_label: "CPU"
+ y_label: "CPU (cores)"
required_metrics: ['container_cpu_usage_seconds_total']
weight: 1
queries:
- query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{id="/"}[15m])) by (job)) without (job)'
- label: Usage
+ label: Usage (cores)
unit: "cores"
appearance:
line:
@@ -15,7 +15,7 @@
area:
opacity: 0
- query_range: 'sum(kube_pod_container_resource_requests_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})'
- label: Requested
+ label: Requested (cores)
unit: "cores"
appearance:
line:
@@ -23,7 +23,7 @@
area:
opacity: 0
- query_range: 'sum(kube_node_status_capacity_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})'
- label: Capacity
+ label: Capacity (cores)
unit: "cores"
appearance:
line:
@@ -32,12 +32,12 @@
area:
opacity: 0
- title: "Memory usage"
- y_label: "Memory"
+ y_label: "Memory (GiB)"
required_metrics: ['container_memory_usage_bytes']
weight: 1
queries:
- query_range: 'avg(sum(container_memory_usage_bytes{id="/"}) by (job)) without (job) / 2^30'
- label: Usage
+ label: Usage (GiB)
unit: "GiB"
appearance:
line:
@@ -45,7 +45,7 @@
area:
opacity: 0
- query_range: 'sum(kube_pod_container_resource_requests_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30'
- label: Requested
+ label: Requested (GiB)
unit: "GiB"
appearance:
line:
@@ -53,7 +53,7 @@
area:
opacity: 0
- query_range: 'sum(kube_node_status_capacity_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30'
- label: Capacity
+ label: Capacity (GiB)
unit: "GiB"
appearance:
line:
diff --git a/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml
index f9ce5a6f365..32475ef8380 100644
--- a/config/prometheus/common_metrics.yml
+++ b/config/prometheus/common_metrics.yml
@@ -21,16 +21,16 @@ panel_groups:
metrics:
- id: response_metrics_nginx_ingress_latency_pod_average
query_range: 'avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"})'
- label: Pod average
+ label: Pod average (ms)
unit: ms
- title: "HTTP Error Rate"
type: "area-chart"
- y_label: "HTTP Errors"
+ y_label: "HTTP Errors (%)"
weight: 1
metrics:
- id: response_metrics_nginx_ingress_http_error_rate
query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
- label: 5xx Errors
+ label: 5xx Errors (%)
unit: "%"
# NGINX Ingress metrics for post-0.16.0 versions
- group: Response metrics (NGINX Ingress)
@@ -52,16 +52,16 @@ panel_groups:
metrics:
- id: response_metrics_nginx_ingress_16_latency_pod_average
query_range: 'sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_sum{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_count{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 1000'
- label: Pod average
+ label: Pod average (ms)
unit: ms
- title: "HTTP Error Rate"
type: "area-chart"
- y_label: "HTTP Errors"
+ y_label: "HTTP Errors (%)"
weight: 1
metrics:
- id: response_metrics_nginx_ingress_16_http_error_rate
query_range: 'sum(rate(nginx_ingress_controller_requests{status=~"5.*",namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_requests{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 100'
- label: 5xx Errors
+ label: 5xx Errors (%)
unit: "%"
- group: Response metrics (HA Proxy)
priority: 10
@@ -82,7 +82,7 @@ panel_groups:
metrics:
- id: response_metrics_ha_proxy_http_error_rate
query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))'
- label: HTTP Errors
+ label: HTTP Errors (%)
unit: "%"
- group: Response metrics (AWS ELB)
priority: 10
@@ -94,7 +94,7 @@ panel_groups:
metrics:
- id: response_metrics_aws_elb_throughput_requests
query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
- label: Total
+ label: Total (req/sec)
unit: req / sec
- title: "Latency"
type: "area-chart"
@@ -103,7 +103,7 @@ panel_groups:
metrics:
- id: response_metrics_aws_elb_latency_average
query_range: 'avg(aws_elb_latency_average{%{environment_filter}}) * 1000'
- label: Average
+ label: Average (ms)
unit: ms
- title: "HTTP Error Rate"
type: "area-chart"
@@ -112,7 +112,7 @@ panel_groups:
metrics:
- id: response_metrics_aws_elb_http_error_rate
query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})'
- label: HTTP Errors
+ label: HTTP Errors (%)
unit: "%"
- group: Response metrics (NGINX)
priority: 10
@@ -133,9 +133,9 @@ panel_groups:
metrics:
- id: response_metrics_nginx_latency
query_range: 'avg(nginx_server_requestMsec{%{environment_filter}})'
- label: Upstream
+ label: Upstream (ms)
unit: ms
- - title: "HTTP Error Rate"
+ - title: "HTTP Error Rate (Errors / Sec)"
type: "area-chart"
y_label: "HTTP 500 Errors / Sec"
weight: 1
@@ -149,12 +149,12 @@ panel_groups:
panels:
- title: "Memory Usage (Total)"
type: "area-chart"
- y_label: "Total Memory Used"
+ y_label: "Total Memory Used (GB)"
weight: 4
metrics:
- id: system_metrics_kubernetes_container_memory_total
query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024'
- label: Total
+ label: Total (GB)
unit: GB
- title: "Core Usage (Total)"
type: "area-chart"
@@ -163,25 +163,25 @@ panel_groups:
metrics:
- id: system_metrics_kubernetes_container_cores_total
query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)'
- label: Total
+ label: Total (cores)
unit: "cores"
- title: "Memory Usage (Pod average)"
type: "area-chart"
- y_label: "Memory Used per Pod"
+ y_label: "Memory Used per Pod (MB)"
weight: 2
metrics:
- id: system_metrics_kubernetes_container_memory_average
query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
- label: Pod average
+ label: Pod average (MB)
unit: MB
- title: "Canary: Memory Usage (Pod Average)"
type: "area-chart"
- y_label: "Memory Used per Pod"
+ y_label: "Memory Used per Pod (MB)"
weight: 2
metrics:
- id: system_metrics_kubernetes_container_memory_average_canary
query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
- label: Pod average
+ label: Pod average (MB)
unit: MB
track: canary
- title: "Core Usage (Pod Average)"
@@ -191,7 +191,7 @@ panel_groups:
metrics:
- id: system_metrics_kubernetes_container_core_usage
query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))'
- label: Pod average
+ label: Pod average (cores)
unit: "cores"
- title: "Canary: Core Usage (Pod Average)"
type: "area-chart"
@@ -200,7 +200,7 @@ panel_groups:
metrics:
- id: system_metrics_kubernetes_container_core_usage_canary
query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))'
- label: Pod average
+ label: Pod average (cores)
unit: "cores"
track: canary
- title: "Knative function invocations"
diff --git a/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb b/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb
index 0048268ca6f..bf7f7b44dec 100644
--- a/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb
+++ b/db/migrate/20190222051615_add_indexes_for_merge_request_diffs_query.rb
@@ -35,7 +35,7 @@ class AddIndexesForMergeRequestDiffsQuery < ActiveRecord::Migration[5.0]
end
def down
- INDEX_SPECS.reverse.each do |spec|
+ INDEX_SPECS.reverse_each do |spec|
remove_concurrent_index(*spec)
end
end
diff --git a/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb b/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb
new file mode 100644
index 00000000000..8963e837e08
--- /dev/null
+++ b/db/migrate/20190611100201_add_geo_container_repository_updated_events_table.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddGeoContainerRepositoryUpdatedEventsTable < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :geo_container_repository_updated_events, force: :cascade do |t|
+ t.integer :container_repository_id, null: false
+
+ t.index :container_repository_id, name: :idx_geo_con_rep_updated_events_on_container_repository_id, using: :btree
+ end
+
+ add_column :geo_event_log, :container_repository_updated_event_id, :bigint
+ end
+end
diff --git a/db/migrate/20190611100202_add_index_to_geo_event_log.rb b/db/migrate/20190611100202_add_index_to_geo_event_log.rb
new file mode 100644
index 00000000000..c5c855fed61
--- /dev/null
+++ b/db/migrate/20190611100202_add_index_to_geo_event_log.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexToGeoEventLog < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :geo_event_log, :container_repository_updated_event_id
+ end
+
+ def down
+ remove_concurrent_index(:geo_event_log, :container_repository_updated_event_id)
+ end
+end
diff --git a/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb b/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb
new file mode 100644
index 00000000000..cf7fd03e9af
--- /dev/null
+++ b/db/migrate/20190627122264_add_foreign_keys_for_container_repository.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddForeignKeysForContainerRepository < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key(:geo_container_repository_updated_events, :container_repositories, column: :container_repository_id, on_delete: :cascade)
+
+ add_concurrent_foreign_key(:geo_event_log, :geo_container_repository_updated_events, column: :container_repository_updated_event_id, on_delete: :cascade)
+ end
+
+ def down
+ if foreign_key_exists?(:geo_container_repository_updated_events, :container_repositories)
+ remove_foreign_key(:geo_container_repository_updated_events, :container_repositories)
+ end
+
+ if foreign_key_exists?(:geo_event_log, :geo_container_repository_updated_events)
+ remove_foreign_key(:geo_event_log, :geo_container_repository_updated_events)
+ end
+ end
+end
diff --git a/db/migrate/20190709220014_import_common_metrics_y_axis.rb b/db/migrate/20190709220014_import_common_metrics_y_axis.rb
new file mode 100644
index 00000000000..89ecf32ecc1
--- /dev/null
+++ b/db/migrate/20190709220014_import_common_metrics_y_axis.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ImportCommonMetricsYAxis < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def up
+ ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb b/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb
new file mode 100644
index 00000000000..81a8b0a3271
--- /dev/null
+++ b/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+# This migration sets up a event enum on the DesignsVersions join table
+class AddEventTypeToDesignManagementDesignsVersions < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ # We disable these cops here because adding this column is safe. The table does not
+ # have any data in it.
+ # rubocop: disable Migration/AddIndex
+ # rubocop: disable Migration/AddColumn
+ def up
+ add_column(:design_management_designs_versions, :event, :integer,
+ limit: 2,
+ null: false,
+ default: 0)
+ add_index(:design_management_designs_versions, :event)
+ end
+
+ # rubocop: disable Migration/RemoveIndex
+ def down
+ remove_index(:design_management_designs_versions, :event)
+ remove_column(:design_management_designs_versions, :event)
+ end
+end
diff --git a/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb b/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb
new file mode 100644
index 00000000000..21b00e0b7d9
--- /dev/null
+++ b/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class ChangeOutboundLocalRequestsWhitelistDefault < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ class ApplicationSetting < ActiveRecord::Base
+ self.table_name = 'application_settings'
+ end
+
+ def up
+ default_value = []
+
+ change_column_default(:application_settings, :outbound_local_requests_whitelist, default_value)
+
+ ApplicationSetting
+ .where(outbound_local_requests_whitelist: nil)
+ .update(outbound_local_requests_whitelist: default_value)
+
+ change_column_null(:application_settings, :outbound_local_requests_whitelist, false)
+ end
+
+ def down
+ change_column_null(:application_settings, :outbound_local_requests_whitelist, true)
+
+ change_column_default(:application_settings, :outbound_local_requests_whitelist, nil)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 430fdd8f708..67479937b47 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_07_15_142138) do
+ActiveRecord::Schema.define(version: 2019_07_25_012225) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -228,7 +228,7 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do
t.boolean "lock_memberships_to_ldap", default: false, null: false
t.boolean "time_tracking_limit_to_hours", default: false, null: false
t.string "grafana_url", default: "/-/grafana", null: false
- t.string "outbound_local_requests_whitelist", limit: 255, array: true
+ t.string "outbound_local_requests_whitelist", limit: 255, default: [], null: false, array: true
t.integer "raw_blob_request_limit", default: 300, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
@@ -1097,8 +1097,10 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do
create_table "design_management_designs_versions", id: false, force: :cascade do |t|
t.bigint "design_id", null: false
t.bigint "version_id", null: false
+ t.integer "event", limit: 2, default: 0, null: false
t.index ["design_id", "version_id"], name: "design_management_designs_versions_uniqueness", unique: true
t.index ["design_id"], name: "index_design_management_designs_versions_on_design_id"
+ t.index ["event"], name: "index_design_management_designs_versions_on_event"
t.index ["version_id"], name: "index_design_management_designs_versions_on_version_id"
end
@@ -1277,6 +1279,11 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do
t.string "key", null: false
end
+ create_table "geo_container_repository_updated_events", force: :cascade do |t|
+ t.integer "container_repository_id", null: false
+ t.index ["container_repository_id"], name: "idx_geo_con_rep_updated_events_on_container_repository_id"
+ end
+
create_table "geo_event_log", force: :cascade do |t|
t.datetime "created_at", null: false
t.bigint "repository_updated_event_id"
@@ -1291,7 +1298,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do
t.bigint "job_artifact_deleted_event_id"
t.bigint "reset_checksum_event_id"
t.bigint "cache_invalidation_event_id"
+ t.bigint "container_repository_updated_event_id"
t.index ["cache_invalidation_event_id"], name: "index_geo_event_log_on_cache_invalidation_event_id", where: "(cache_invalidation_event_id IS NOT NULL)"
+ t.index ["container_repository_updated_event_id"], name: "index_geo_event_log_on_container_repository_updated_event_id"
t.index ["hashed_storage_attachments_event_id"], name: "index_geo_event_log_on_hashed_storage_attachments_event_id", where: "(hashed_storage_attachments_event_id IS NOT NULL)"
t.index ["hashed_storage_migrated_event_id"], name: "index_geo_event_log_on_hashed_storage_migrated_event_id", where: "(hashed_storage_migrated_event_id IS NOT NULL)"
t.index ["job_artifact_deleted_event_id"], name: "index_geo_event_log_on_job_artifact_deleted_event_id", where: "(job_artifact_deleted_event_id IS NOT NULL)"
@@ -3702,7 +3711,9 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do
add_foreign_key "fork_network_members", "projects", on_delete: :cascade
add_foreign_key "fork_networks", "projects", column: "root_project_id", name: "fk_e7b436b2b5", on_delete: :nullify
add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade
+ add_foreign_key "geo_container_repository_updated_events", "container_repositories", name: "fk_212c89c706", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_cache_invalidation_events", column: "cache_invalidation_event_id", name: "fk_42c3b54bed", on_delete: :cascade
+ add_foreign_key "geo_event_log", "geo_container_repository_updated_events", column: "container_repository_updated_event_id", name: "fk_6ada82d42a", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_hashed_storage_migrated_events", column: "hashed_storage_migrated_event_id", name: "fk_27548c6db3", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_job_artifact_deleted_events", column: "job_artifact_deleted_event_id", name: "fk_176d3fbb5d", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_lfs_object_deleted_events", column: "lfs_object_deleted_event_id", name: "fk_d5af95fcd9", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index bf7017d8fb4..af675582a99 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -302,7 +302,7 @@ The following documentation relates to the DevOps **Configure** stage:
| Configure Topics | Description |
|:-----------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
| [Auto DevOps](topics/autodevops/index.md) | Automatically employ a complete DevOps lifecycle. |
-| [Easy creation of Kubernetes<br/>clusters on GKE](user/project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab) | Use Google Kubernetes Engine and GitLab. |
+| [Create Kubernetes clusters on GKE](user/project/clusters/index.md#add-new-gke-cluster) | Use Google Kubernetes Engine and GitLab. |
| [Executable Runbooks](user/project/clusters/runbooks/index.md) | Documented procedures that explain how to carry out particular processes. |
| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
| [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. |
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 44a33eb7bcf..31876dd178a 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -242,7 +242,7 @@ I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory
User clone/fetch activity using ssh transport appears in this log as `executing git command <gitaly-upload-pack...`.
-## `unicorn\_stderr.log`
+## `unicorn_stderr.log`
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/unicorn_stderr.log` for
diff --git a/doc/api/README.md b/doc/api/README.md
index 91bc8ca8aab..70540420544 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -515,7 +515,7 @@ more than 10,000, the `X-Total` and `X-Total-Pages` headers as well as the
## Namespaced path encoding
-If using namespaced API calls, make sure that the `NAMESPACE/PROJECT_NAME` is
+If using namespaced API calls, make sure that the `NAMESPACE/PROJECT_PATH` is
URL-encoded.
For example, `/` is represented by `%2F`:
@@ -524,6 +524,11 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/diaspora%2Fdiaspora
```
+NOTE: **Note:**
+A project's **path** is not necessarily the same as its **name**. A
+project's path can found in the project's URL or in the project's settings
+under **General > Advanced > Change path**.
+
## Branches and tags name encoding
If your branch or tag contains a `/`, make sure the branch/tag name is
diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md
index ed5ebdade19..2496b038c7f 100644
--- a/doc/api/dependencies.md
+++ b/doc/api/dependencies.md
@@ -17,8 +17,8 @@ supported by Gemnasium.
```
GET /projects/:id/dependencies
-GET /projects/:id/vulnerabilities?package_manger=maven
-GET /projects/:id/vulnerabilities?package_manger=yarn,bundler
+GET /projects/:id/vulnerabilities?package_manager=maven
+GET /projects/:id/vulnerabilities?package_manager=yarn,bundler
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md
index 31c0e6abead..29e58d9279a 100644
--- a/doc/api/group_clusters.md
+++ b/doc/api/group_clusters.md
@@ -210,7 +210,7 @@ Parameters:
NOTE: **Note:**
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
-through the ["Add an existing Kubernetes Cluster"](../user/project/clusters/index.md#adding-an-existing-kubernetes-cluster) option or
+through the ["Add existing Kubernetes cluster"](../user/project/clusters/index.md#add-existing-kubernetes-cluster) option or
through the ["Add existing cluster to group"](#add-existing-cluster-to-group) endpoint.
Example request:
diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md
index 614ea41d572..762a4ad95ab 100644
--- a/doc/api/project_clusters.md
+++ b/doc/api/project_clusters.md
@@ -261,7 +261,7 @@ Parameters:
NOTE: **Note:**
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
-through the ["Add an existing Kubernetes Cluster"](../user/project/clusters/index.md#adding-an-existing-kubernetes-cluster) option or
+through the ["Add existing Kubernetes cluster"](../user/project/clusters/index.md#add-existing-kubernetes-cluster) option or
through the ["Add existing cluster to project"](#add-existing-cluster-to-project) endpoint.
Example request:
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 7048ceaac41..6923d07bb1d 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -13,6 +13,11 @@ through the [continuous methodologies](introduction/index.md#introduction-to-cic
- Continuous Delivery (CD)
- Continuous Deployment (CD)
+NOTE: **Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.**
+Watch our
+["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
+webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
+
## Overview
Continuous Integration works by pushing small code chunks to your
@@ -155,6 +160,7 @@ for your CI/CD infrastructure:
- [Why we chose GitLab CI for our CI/CD solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/)
- [Building our web-app on GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/)
+- [5 Teams that made the switch to GitLab CI/CD](https://about.gitlab.com/2019/04/25/5-teams-that-made-the-switch-to-gitlab-ci-cd/)
See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJaIOzCX4Vqg3dlwfELC3u2jEeCBbDk) presentation.
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index 6401ff90a0a..366aca3442e 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -9,6 +9,11 @@ In this document we'll present an overview of the concepts of Continuous Integra
Continuous Delivery, and Continuous Deployment, as well as an introduction to
GitLab CI/CD.
+NOTE: **Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.**
+Watch our
+["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
+webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
+
## Introduction to CI/CD methodologies
The continuous methodologies of software development are based on
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
index f63b17a9e5a..ad07c662965 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
@@ -62,18 +62,32 @@ CAUTION: **Warning:**
Make sure your `gitlab-ci.yml` file is [configured properly for pipelines for merge requests](../index.md#configuring-pipelines-for-merge-requests),
otherwise pipelines for merged results won't run and your merge requests will be stuck in an unresolved state.
-## Merge Trains **(PREMIUM)**
+## Troubleshooting
-Read the [documentation on Merge Trains](merge_trains/index.md).
+### Pipelines for merged results not created even with new change pushed to merge request
-<!-- ## Troubleshooting
+Can be caused by some disabled feature flags. Please make sure that
+the following feature flags are enabled on your GitLab instance:
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+- `:ci_use_merge_request_ref`
+- `:merge_ref_auto_sync`
-Each scenario can be a third-level heading, e.g. `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
+To check these feature flag values, please ask administrator to execute the following commands:
+
+```shell
+> sudo gitlab-rails console # Login to Rails console of GitLab instance.
+> Feature.enabled?(:ci_use_merge_request_ref) # Check if it's enabled or not.
+> Feature.enable(:ci_use_merge_request_ref) # Enable the feature flag.
+```
+
+## Using Merge Trains **(PREMIUM)**
+
+By enabling [Pipelines for merged results](#pipelines-for-merged-results-premium),
+GitLab will [automatically display](merge_trains/index.md#how-to-add-a-merge-request-to-a-merge-train)
+a **Start/Add Merge Train button** as the most recommended merge strategy.
+
+Generally, this is a safer option than merging merge requests immediately as your
+merge request will be evaluated with an expected post-merge result before the actual
+merge happens.
+
+For more information, read the [documentation on Merge Trains](merge_trains/index.md).
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png
new file mode 100644
index 00000000000..a8916e5721c
--- /dev/null
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png
Binary files differ
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png
new file mode 100644
index 00000000000..65ff7e3d674
--- /dev/null
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png
Binary files differ
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
index 44cbcde264c..80a1c264bc4 100644
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
+++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
@@ -80,14 +80,65 @@ button while the latest pipeline is running.
![Add to merge train when pipeline succeeds](img/merge_train_start_when_pipeline_succeeds_v12_0.png)
-<!-- ## Troubleshooting
+## Immediately merge a merge request with a merge train
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+In case, you have a high-priority merge request (e.g. critical patch) to be merged urgently,
+you can use **Merge Immediately** option for bypassing the merge train.
+This is the fastest option to get the change merged into the target branch.
-Each scenario can be a third-level heading, e.g. `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
+![Merge Immediately](img/merge_train_immediate_merge.png)
+
+However, every time you merge a merge request immediately, it could affect the
+existing merge train to be reconstructed, specifically, it regenerates expected
+merge commits and pipelines. This means, merging immediately essentially wastes
+CI resources.
+
+## Troubleshooting
+
+### Merge request dropped from the merge train immediately
+
+If a merge request is not mergeable (for example, it's WIP, there is a merge
+conflict, etc), your merge request will be dropped from the merge train automatically.
+
+In these cases, the reason for dropping the merge request is in the **system notes**.
+
+To check the reason:
+
+1. Open the merge request that was dropped from the merge train.
+1. Open the **Discussion** tab.
+1. Find a system note that includes either:
+ - The text **... removed this merge request from the merge train because ...**
+ - **... aborted this merge request from the merge train because ...**
+ The reason is given in the text after the **because ...** phrase.
+
+![Merge Train Failure](img/merge_train_failure.png)
+
+### Merge When Pipeline Succeeds cannot be chosen
+
+[Merge When Pipeline Succeeds](../../../../user/project/merge_requests/merge_when_pipeline_succeeds.md)
+is unavailable when
+[Pipelines for Merged Results is enabled](../index.md#enabling-pipelines-for-merged-results).
+
+Follow [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12267) to
+track progress on this issue.
+
+### Merge Train disturbs your workflow
+
+First of all, please check if [merge immediately](#immediately-merge-a-merge-request-with-a-merge-train)
+is available as a workaround in your workflow. This is the most recommended
+workaround you'd be able to take immediately. If it's not available or acceptable,
+please read through this section.
+
+Merge train is enabled by default when you enable [Pipelines for merged results](../index.md),
+however, you can forcibly disable this feature by disabling the feature flag `:merge_trains_enabled`.
+After you disabled this feature, all the existing merge trains will be aborted and
+you will no longer see the **Start/Add Merge Train** button in merge requests.
+
+To check if the feature flag is enabled on your GitLab instance,
+please ask administrator to execute the following commands:
+
+```shell
+> sudo gitlab-rails console # Login to Rails console of GitLab instance.
+> Feature.enabled?(:merge_trains_enabled) # Check if it's enabled or not.
+> Feature.disable(:merge_trains_enabled) # Disable the feature flag.
+``` \ No newline at end of file
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 98f30350968..be8f66c741f 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -6,6 +6,11 @@ type: reference
> Introduced in GitLab 8.8.
+NOTE: **Tip:**
+Watch our
+["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
+webcast to see a comprehensive demo of GitLab CI/CD pipeline.
+
## Introduction
Pipelines are the top-level component of continuous integration, delivery, and deployment.
diff --git a/doc/development/README.md b/doc/development/README.md
index ea5d9e10e2c..99c88146be5 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -113,6 +113,10 @@ description: 'Learn how to contribute to GitLab.'
- [Database helper modules](database_helpers.md)
- [Code comments](code_comments.md)
+## Case studies
+
+- [Database case study: Filtering by label](filtering_by_label.md)
+
## Integration guides
- [Jira Connect app](integrations/jira_connect.md)
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 709cd0c806b..b7d74b17eb3 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -365,6 +365,31 @@ Enterprise Edition instance. This has some implications:
1. **Filesystem access** can be slow, so try to avoid
[shared files](shared_files.md) when an alternative solution is available.
+## Examples
+
+How code reviews are conducted can surprise new contributors. Here are some examples of code reviews that should help to orient you as to what to expect.
+
+**["Modify `DiffNote` to reuse it for Designs"](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13703):**
+It contained everything from nitpicks around newlines to reasoning
+about what versions for designs are, how we should compare them
+if there was no previous version of a certain file (parent vs.
+blank `sha` vs empty tree).
+
+**["Support multi-line suggestions"](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/25211)**:
+The MR itself consists of a collaboration between FE and BE,
+and documenting comments from the author for the reviewer.
+There's some nitpicks, some questions for information, and
+towards the end, a security vulnerability.
+
+**["Allow multiple repositories per project"](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/10251)**:
+ZJ referred to the other projects (workhorse) this might impact,
+suggested some improvements for consistency. And James' comments
+helped us with overall code quality (using delegation, `&.` those
+types of things), and making the code more robust.
+
+**["Support multiple assignees for merge requests"](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/10161)**:
+A good example of collaboration on an MR touching multiple parts of the codebase. Nick pointed out interesting edge cases, James Lopes also joined in raising concerns on import/export feature.
+
### Credits
Largely based on the [thoughtbot code review guide].
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index 1413c2f69fb..3d10a0c84e5 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -68,6 +68,17 @@ make sure you have applied the ~database label and rerun the
`danger-review` CI job, or pick someone from the
[`@gl-database` team](https://gitlab.com/groups/gl-database/-/group_members).
+### How to prepare for speedy database reviews
+
+In order to make reviewing easier and therefore faster, please consider preparing a comment
+and details for a database reviewer:
+
+- Provide queries in SQL form rather than ActiveRecord.
+- Format any queries with a SQL query formatter, for example with [sqlformat.darold.net](http://sqlformat.darold.net).
+- Consider providing query plans via a link to [explain.depesz.com](https://explain.depesz.com) or another tool instead of textual form.
+- For query changes, it is best to provide the SQL query along with a plan *before* and *after* the change. This helps to spot differences quickly.
+- When providing query plans, make sure to use good parameter values, so that the query executed is a good example and also hits enough data. Usually, the `gitlab-org` namespace (`namespace_id = 9970`) and the `gitlab-org/gitlab-ce` project (`project_id = 13083`) provides enough data to serve as a good example.
+
### How to review for database
- Check migrations
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 8ce855594df..d717b17136e 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -57,35 +57,22 @@ See the [Structure](styleguide.md#structure) section of the [Documentation Style
We currently maintain two sets of docs: one in the
[gitlab-ce](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc) repo and
one in [gitlab-ee](https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc).
-They are similar, and most pages are identical, but they are different repositories.
-With the single codebase effort, we want to make those two sets identical, so when the
-time comes to have only one codebase, we'll be ready.
-
-Here are some links to get you up to speed with the current effort:
-
-- [CE/EE codebases blueprint](https://about.gitlab.com/handbook/engineering/infrastructure/blueprint/ce-ee-codebases/)
-- [CE/EE codebases merge design](https://about.gitlab.com/handbook/engineering/infrastructure/design/merge-ce-ee-codebases/)
-- [Single docs codebase epic](https://gitlab.com/groups/gitlab-org/-/epics/199)
-- [Issue board of related issues](https://gitlab.com/groups/gitlab-org/-/boards/981090?&label_name[]=Documentation&label_name[]=single%20codebase)
-- [Related merge requests](https://gitlab.com/groups/gitlab-org/-/merge_requests?scope=all&utf8=%E2%9C%93&state=all&label_name[]=Documentation&label_name[]=single%20codebase)
-- [Visualize the existing diffs](https://leipert-projects.gitlab.io/is-gitlab-pretty-yet/diff/?search=%5Edoc)
+They are identical, but they are different repositories. When the
+time comes to have only one codebase for the GitLab project, we'll be ready.
### CE first
-After a given documentation path is aligned across CE and EE, all merge requests
-affecting that path must be submitted to CE, regardless of the content it has.
-This means that:
+All merge requests for documentation must be submitted to CE, regardless of the content
+it has. This means that:
-- For **EE-only docs changes**, you only have to submit a CE MR.
+- For **EE-only docs changes**, you only have to submit an MR in the CE project.
- For **EE-only features** that touch both the code and the docs, you have to submit
- an EE MR containing all changes, and a CE MR containing only the docs changes
+ an EE MR containing all code changes, and a CE MR containing only the docs changes
and without a changelog entry.
This might seem like a duplicate effort, but it's only for the short term.
-A list of the already aligned docs can be found in
-[the epic description](https://gitlab.com/groups/gitlab-org/-/epics/199#ee-specific-lines-check).
-Since the docs will be combined, it's crucial to add the relevant
+Since the CE and EE docs are combined, it's crucial to add the relevant
[product badges](styleguide.md#product-badges) for all EE documentation, so that
we can discern which features belong to which tier.
@@ -94,19 +81,9 @@ we can discern which features belong to which tier.
There's a special test in place
([`ee_specific_check.rb`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/scripts/ee_specific_check/ee_specific_check.rb)),
which, among others, checks and prevents creating/editing new files and directories
-in EE under `doc/`.
-
-We have a long list of documentation paths that are either whitelisted or not.
-Paths in the whitelist (not commented out) will not be subject to the test,
-which means you are allowed to create/change docs content in EE for the time
-being. The goal is to not have any doc whitelisted.
-
-At the time of this writing, the only items left to be aligned are the API docs:
-
-- `doc/api/*` ([issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/60045) / [merge request](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27491))
-
-Eventually, once all docs are aligned, we'll remove any doc reference from that
-script, so it catches everything.
+in EE under `doc/`. This should fail when changes to anything in `/doc` are submitted
+in an EE MR. To pass the test, simply remove the docs changes from the EE MR, and
+[submit them in CE](#ce-first).
## Changing document location
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 36ffc02644e..dd798777c12 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -1092,6 +1092,6 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "domain_
[cURL]: http://curl.haxx.se/ "cURL website"
[single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html
-[gfm]: https://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation"
+[gfm]: ../../user/markdown.md#newlines "GitLab flavored markdown documentation"
[ce-1242]: https://gitlab.com/gitlab-org/gitlab-ce/issues/1242
[doc-restart]: ../../administration/restart_gitlab.md "GitLab restart documentation"
diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md
new file mode 100644
index 00000000000..6e6b71b1787
--- /dev/null
+++ b/doc/development/filtering_by_label.md
@@ -0,0 +1,166 @@
+# Filtering by label
+
+## Introduction
+
+GitLab has [labels](../user/project/labels.md) that can be assigned to issues,
+merge requests, and epics. Labels on those objects are a many-to-many relation
+through the polymorphic `label_links` table.
+
+To filter these objects by multiple labels - for instance, 'all open
+issues with the label ~Plan and the label ~backend' - we generate a
+query containing a `GROUP BY` clause. In a simple form, this looks like:
+
+```sql
+SELECT
+ issues.*
+FROM
+ issues
+ INNER JOIN label_links ON label_links.target_id = issues.id
+ AND label_links.target_type = 'Issue'
+ INNER JOIN labels ON labels.id = label_links.label_id
+WHERE
+ issues.project_id = 13083
+ AND (issues.state IN ('opened'))
+ AND labels.title IN ('Plan',
+ 'backend')
+GROUP BY
+ issues.id
+HAVING (COUNT(DISTINCT labels.title) = 2)
+ORDER BY
+ issues.updated_at DESC,
+ issues.id DESC
+LIMIT 20 OFFSET 0
+```
+
+In particular, note that:
+
+1. We `GROUP BY issues.id` so that we can ...
+2. Use the `HAVING (COUNT(DISTINCT labels.title) = 2)` condition to ensure that
+ all matched issues have both labels.
+
+This is more complicated than is ideal. It makes the query construction more
+prone to errors (such as
+[gitlab-org/gitlab-ce#15557](https://gitlab.com/gitlab-org/gitlab-ce/issues/15557)).
+
+## Attempt A: WHERE EXISTS
+
+### Attempt A1: use multiple subqueries with WHERE EXISTS
+
+In
+[gitlab-org/gitlab-ce#37137](https://gitlab.com/gitlab-org/gitlab-ce/issues/37137)
+and its associated merge request
+[gitlab-org/gitlab-ce!14022](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14022),
+we tried to replace the `GROUP BY` with multiple uses of `WHERE EXISTS`. For the
+example above, this would give:
+
+```sql
+WHERE (EXISTS (
+ SELECT
+ TRUE
+ FROM
+ label_links
+ INNER JOIN labels ON labels.id = label_links.label_id
+ WHERE
+ labels.title = 'Plan'
+ AND target_type = 'Issue'
+ AND target_id = issues.id))
+AND (EXISTS (
+ SELECT
+ TRUE
+ FROM
+ label_links
+ INNER JOIN labels ON labels.id = label_links.label_id
+ WHERE
+ labels.title = 'backend'
+ AND target_type = 'Issue'
+ AND target_id = issues.id))
+```
+
+While this worked without schema changes, and did improve readability somewhat,
+it did not improve query performance.
+
+## Attempt B: Denormalize using an array column
+
+Having [removed MySQL support in GitLab
+12.1](https://about.gitlab.com/2019/06/27/removing-mysql-support/), using
+[Postgres's arrays](https://www.postgresql.org/docs/9.6/arrays.html) became more
+tractable as we didn't have to support two databases. We discussed denormalizing
+the `label_links` table for querying in
+[gitlab-org/gitlab-ce#49651](https://gitlab.com/gitlab-org/gitlab-ce/issues/49651),
+with two options: label IDs and titles.
+
+We can think of both of those as array columns on `issues`, `merge_requests`,
+and `epics`: `issues.label_ids` would be an array column of label IDs, and
+`issues.label_titles` would be an array of label titles.
+
+These array columns can be complemented with [GIN
+indexes](https://www.postgresql.org/docs/9.6/gin-intro.html) to improve
+matching.
+
+### Attempt B1: store label IDs for each object
+
+This has some strong advantages over titles:
+
+1. Unless a label is deleted, or a project is moved, we never need to
+ bulk-update the denormalized column.
+2. It uses less storage than the titles.
+
+Unfortunately, our application design makes this hard. If we were able to query
+just by label ID easily, we wouldn't need the `INNER JOIN labels` in the initial
+query at the start of this document. GitLab allows users to filter by label
+title across projects and even across groups, so a filter by the label ~Plan may
+include labels with multiple distinct IDs.
+
+We do not want users to have to know about the different IDs, which means that
+given this data set:
+
+| Project | ~Plan label ID | ~backend label ID |
+| --- | --- | --- |
+| A | 11 | 12 |
+| B | 21 | 22 |
+| C | 31 | 32 |
+
+We would need something like:
+
+```sql
+WHERE
+ label_ids @> ARRAY[11, 12]
+ OR label_ids @> ARRAY[21, 22]
+ OR label_ids @> ARRAY[31, 32]
+```
+
+This can get even more complicated when we consider that in some cases, there
+might be two ~backend labels - with different IDs - that could apply to the same
+object, so the number of combinations would balloon further.
+
+### Attempt B2: store label titles for each object
+
+From the perspective of updating the labelable object, this is the worst
+option. We have to bulk update the objects when:
+
+1. The objects are moved from one project to another.
+1. The project is moved from one group to another.
+1. The label is renamed.
+1. The label is deleted.
+
+It also uses much more storage. Querying is simple, though:
+
+```sql
+WHERE
+ label_titles @> ARRAY['Plan', 'backend']
+```
+
+And our [tests in
+gitlab-org/gitlab-ce#49651](https://gitlab.com/gitlab-org/gitlab-ce/issues/49651#note_188777346)
+showed that this could be fast.
+
+However, at present, the disadvantages outweigh the advantages.
+
+## Conclusion
+
+We have yet to find a method that is demonstratably better than the current
+method, when considering:
+
+1. Query performance.
+1. Readability.
+1. Ease of maintaining schema consistency.
diff --git a/doc/development/new_fe_guide/dependencies.md b/doc/development/new_fe_guide/dependencies.md
index 12a4f089d41..8a6930acd37 100644
--- a/doc/development/new_fe_guide/dependencies.md
+++ b/doc/development/new_fe_guide/dependencies.md
@@ -15,6 +15,18 @@ Exceptions are made for some tools that we require in the
`gitlab:assets:compile` CI job such as `webpack-bundle-analyzer` to analyze our
production assets post-compile.
+To add or upgrade a dependency, run:
+
+```sh
+yarn add <your dependency here>
+```
+
+This may introduce duplicate dependencies. To de-duplicate `yarn.lock`, run:
+
+```sh
+node_modules/.bin/yarn-deduplicate --list --strategy fewer yarn.lock && yarn install
+```
+
---
> TODO: Add Dependencies
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index ff28c2ea5e2..2985278cc92 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -588,7 +588,7 @@ end
[jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html
[karma]: http://karma-runner.github.io/
-[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
+[vue-test]: ../fe_guide/vue.md#testing-vue-components
[rspec]: https://github.com/rspec/rspec-rails#feature-specs
[capybara]: https://github.com/teamcapybara/capybara
[jasmine]: https://jasmine.github.io/
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index c0e1b0ebbc8..543a222bd25 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -70,7 +70,7 @@ The first items we need to configure are the basic settings of the underlying vi
> **Note:** if you're unsure which authentication type to use, select **Password**
1. If you chose **SSH public key** - enter your `SSH public key` into the field provided
- _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH
+ _(read the [SSH documentation](../../ssh/README.md) to learn more about how to set up SSH
public keys)_
1. If you chose **Password** - enter the password you wish to use _(this is the password that you
will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_
@@ -407,7 +407,7 @@ on any cloud service you choose.
## Where to next?
-Check out our other [Technical Articles][GitLab-Technical-Articles] or browse the [GitLab Documentation][GitLab-Docs] to learn more about GitLab.
+Check out our other [Technical Articles](../../articles/index.md) or browse the [GitLab Documentation][GitLab-Docs](../../README.md) to learn more about GitLab.
### Useful links
@@ -423,9 +423,6 @@ Check out our other [Technical Articles][GitLab-Technical-Articles] or browse th
- [SSH], [PuTTY] and [Using SSH in PuTTY][Using-SSH-In-Putty]
[Original-Blog-Post]: https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/ "How to Set up a GitLab Instance on Microsoft Azure"
-[GitLab-Docs]: https://docs.gitlab.com/ce/README.html "GitLab Documentation"
-[GitLab-Technical-Articles]: https://docs.gitlab.com/ce/articles/index.html "GitLab Technical Articles"
-[GitLab-Docs-SSH]: https://docs.gitlab.com/ce/ssh/README.html "GitLab Documentation: SSH"
[CE]: https://about.gitlab.com/features/
[EE]: https://about.gitlab.com/features/#ee-starter
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 135952a1b08..55f9666e3a3 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -27,7 +27,7 @@ See the documentation below for details on how to configure these services.
- [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider
- [Trello](trello_power_up.md) Integrate Trello with GitLab
-> GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].
+> GitLab Enterprise Edition contains [advanced Jenkins support](jenkins.md).
## Project services
@@ -70,5 +70,3 @@ After that restart GitLab with:
```bash
sudo gitlab-ctl restart
```
-
-[jenkins]: https://docs.gitlab.com/ee/integration/jenkins.html
diff --git a/doc/integration/slack.md b/doc/integration/slack.md
index f84ab769218..9fcf2c2d99a 100644
--- a/doc/integration/slack.md
+++ b/doc/integration/slack.md
@@ -1,5 +1,5 @@
---
-redirect_to: '../project_services/slack.md'
+redirect_to: '../user/project/integrations/slack.md'
---
-This document was moved to [project_services/slack.md](../project_services/slack.md).
+This document was moved to [project_services/slack.md](../user/project/integrations/slack.md).
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index c416dbac492..c6219126b30 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -192,10 +192,10 @@ The following table is an example of how the three different clusters would
be configured.
| Cluster name | Cluster environment scope | `KUBE_INGRESS_BASE_DOMAIN` variable value | Variable environment scope | Notes |
-| ------------ | -------------- | ----------------------------- | ------------- | ------ |
-| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
-| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
-| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). |
+|--------------|---------------------------|-------------------------------------------|----------------------------|---|
+| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
+| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
+| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). |
To add a different cluster for each environment:
@@ -665,10 +665,6 @@ to the desired environment. See [Limiting environment scopes of variables](../..
### Customizing `.gitlab-ci.yml`
-Everything about Auto DevOps is customizable since the [Auto DevOps template]
-is just an example of a [`.gitlab-ci.yml`](../../ci/yaml/README.md) and uses
-only features that are available to any `.gitlab-ci.yml`.
-
Auto DevOps is completely customizable because the [Auto DevOps template]:
- Is just an implementation of a [`.gitlab-ci.yml`](../../ci/yaml/README.md) file.
@@ -725,47 +721,47 @@ The following variables can be used for setting up the Auto DevOps domain,
providing a custom Helm chart, or scaling your application. PostgreSQL can
also be customized, and you can easily use a [custom buildpack](#custom-buildpacks).
-| **Variable** | **Description** |
-| ------------ | --------------- |
-| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). |
-| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
-| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From Gitlab 11.11, this variable can be used to set the name of the helm repository; defaults to "gitlab" |
+| **Variable** | **Description** |
+|-----------------------------------------|------------------------------------|
+| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). |
+| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
+| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From Gitlab 11.11, this variable can be used to set the name of the helm repository; defaults to "gitlab" |
| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From Gitlab 11.11, this variable can be used to set a username to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD) |
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From Gitlab 11.11, this variable can be used to set a password to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_USERNAME) |
-| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
-| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
-| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1 |
-| `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1 |
-| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. |
-| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. |
-| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled; defaults to `"true"`. Set to `false` to disable the automatic deployment of PostgreSQL. |
-| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. |
-| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
-| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. |
-| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.2`. |
-| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
-| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.|
-| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).|
-| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. |
-| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
-| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
-| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
-| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
-| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
-| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
-| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. |
-| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
-| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
-| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
-| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
-| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
-| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
-| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. |
-| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. |
-| `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. |
-| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. |
-| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](https://docs.gitlab.com/ee/topics/autodevops/index.html#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. |
-| `HELM_RELEASE_NAME` | From GitLab 12.1, this variable allows the `helm` release name to be overridden, this can be used to assign unique release names when deploying multiple projects to a single namespace |
+| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
+| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. |
+| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1. |
+| `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1. |
+| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. |
+| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. |
+| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled; defaults to `"true"`. Set to `false` to disable the automatic deployment of PostgreSQL. |
+| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. |
+| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
+| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. |
+| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.2`. |
+| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142`. |
+| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`. |
+| `DS_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](../../user/application_security/dependency_scanning/index.md#remote-checks). |
+| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. |
+| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
+| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
+| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
+| `INCREMENTAL_ROLLOUT_MODE` | From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
+| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
+| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
+| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. |
+| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
+| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
+| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
+| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
+| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
+| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
+| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. |
+| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. |
+| `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. |
+| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. |
+| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. |
+| `HELM_RELEASE_NAME` | From GitLab 12.1, this variable allows the `helm` release name to be overridden, this can be used to assign unique release names when deploying multiple projects to a single namespace. |
TIP: **Tip:**
Set up the replica variables using a
@@ -1058,7 +1054,7 @@ planned for a subsequent release.
case, you may need to customize your `.gitlab-ci.yml` with your test commands.
- Auto Deploy will fail if GitLab can not create a Kubernetes namespace and
service account for your project. For help debugging this issue, see
- [Troubleshooting failed deployment jobs](../../user/project/clusters/index.md#troubleshooting-failed-deployment-jobs).
+ [Troubleshooting failed deployment jobs](../../user/project/clusters/index.md#troubleshooting).
### Disable the banner instance wide
diff --git a/doc/university/README.md b/doc/university/README.md
index f696db2df20..b17f40b91a5 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -9,6 +9,10 @@ GitLab University is a great place to start when learning about version control
If you're looking for a GitLab subscription for _your university_, see our [Education](https://about.gitlab.com/solutions/education/) page.
+CAUTION: **Caution:**
+Some of the content in GitLab University may be out of date and we plan to
+[evaluate](https://gitlab.com/gitlab-org/gitlab-ce/issues/41064) it.
+
The GitLab University curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections:
1. [GitLab Beginner](#1-gitlab-beginner).
diff --git a/doc/user/admin_area/abuse_reports.md b/doc/user/admin_area/abuse_reports.md
index 8088c33fc9c..0c5d2f81e25 100644
--- a/doc/user/admin_area/abuse_reports.md
+++ b/doc/user/admin_area/abuse_reports.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Abuse reports
View and resolve abuse reports from GitLab users.
@@ -59,3 +63,15 @@ page:
NOTE: **Note:**
Users can be [blocked](../../api/users.md#block-user) and
[unblocked](../../api/users.md#unblock-user) using the GitLab API.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/admin_area/broadcast_messages.md b/doc/user/admin_area/broadcast_messages.md
index 02445abdb37..01b6558bdbe 100644
--- a/doc/user/admin_area/broadcast_messages.md
+++ b/doc/user/admin_area/broadcast_messages.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Broadcast Messages
GitLab can display messages to all users of a GitLab instance in a banner that appears in the UI.
@@ -51,3 +55,15 @@ Once deleted, the broadcast message is removed from the list of broadcast messag
NOTE: **Note:**
Broadcast messages can be deleted while active.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md
index 427f3103cfc..02c2efaa4f3 100644
--- a/doc/user/admin_area/custom_project_templates.md
+++ b/doc/user/admin_area/custom_project_templates.md
@@ -1,26 +1,49 @@
+---
+type: reference
+---
+
# Custom instance-level project templates **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2.
-When you create a new [project](../project/index.md), creating it based on custom project templates is
-a convenient bootstrap option.
+GitLab administrators can configure the group where all the custom project
+templates are sourced.
-GitLab administrators can configure a GitLab group that serves as template
-source for an entire GitLab instance under **Admin area > Settings > Custom project templates**.
+Every project directly under the group namespace will be
+available to the user if they have access to them. For example:
+
+- Public project in the group will be available to every logged in user.
+- Private projects will be available only if the user is a member of the project.
+
+Repository and database information that are copied over to each new project are
+identical to the data exported with
+[GitLab's Project Import/Export](../project/settings/import_export.md).
NOTE: **Note:**
To set project templates at a group level,
see [Custom group-level project templates](../group/custom_project_templates.md).
-Within this section, you can configure the group where all the custom project
-templates are sourced. Every project directly under the group namespace will be
-available to the user if they have access to them. For example, every public
-project in the group will be available to every logged in user.
+## Configuring
-However, private projects will be available only if the user is a member of the project.
+GitLab administrators can configure a GitLab group that serves as template
+source for an entire GitLab instance by:
+
+1. Navigating to **Admin area > Settings > Templates**.
+1. Expanding **Custom project templates**.
+1. Selecting a group to use.
+1. Pressing **Save changes**.
NOTE: **Note:**
Projects below subgroups of the template group are **not** supported.
-Repository and database information that are copied over to each new project are
-identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md).
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index da75684a3fe..86491c7d74e 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Container Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3672)
@@ -47,7 +51,7 @@ To enable Container Scanning in your pipeline, you need:
your Docker image to your project's [Container Registry](../../project/container_registry.md).
The name of the Docker image should match the following scheme:
- ```
+ ```text
$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
```
@@ -114,7 +118,7 @@ When the GitLab Runner uses the Docker executor and NFS is used
(e.g., `/var/lib/docker` is on an NFS mount), Container Scanning might fail with
an error like the following:
-```
+```text
docker: Error response from daemon: failed to copy xattrs: failed to set xattr "security.selinux" on /path/to/file: operation not supported.
```
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 4b98dd73d76..88e2d1ef22b 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Dynamic Application Security Testing (DAST) **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4348)
@@ -199,3 +203,15 @@ Once a vulnerability is found, you can interact with it. Read more on how to
For more information about the vulnerabilities database update, check the
[maintenance table](../index.md#maintenance-and-update-of-the-vulnerabilities-database).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 7473647f129..6a810757a28 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Dependency Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5105)
@@ -150,7 +154,7 @@ using environment variables.
| `DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT` | Time limit for Docker client negotiation. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
| `DS_PULL_ANALYZER_IMAGE_TIMEOUT` | Time limit when pulling the image of an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
| `DS_RUN_ANALYZER_TIMEOUT` | Time limit when running an analyzer. Timeouts are parsed using Go's [`ParseDuration`](https://golang.org/pkg/time/#ParseDuration). Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For example, `300ms`, `1.5h`, or `2h45m`. |
-| `PIP_INDEX_URL` | Base URL of Python Package Index (default https://pypi.org/simple). |
+| `PIP_INDEX_URL` | Base URL of Python Package Index (default `https://pypi.org/simple`). |
| `PIP_EXTRA_INDEX_URL` | Array of [extra URLs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-extra-index-url) of package indexes to use in addition to `PIP_INDEX_URL`. Comma separated. |
## Reports JSON format
@@ -331,7 +335,7 @@ project's dependencies with their versions. This list can be generated only for
[languages and package managers](#supported-languages-and-package-managers)
supported by Gemnasium.
-To see the generated dependency list, navigate to your project's **Project > Dependency List**.
+To see the generated dependency list, navigate to your project's **Security & Compliance > Dependency List**.
## Versioning and release process
@@ -342,3 +346,15 @@ Please check the [Release Process documentation](https://gitlab.com/gitlab-org/s
You can search the [gemnasium-db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) project
to find a vulnerability in the Gemnasium database.
You can also [submit new vulnerabilities](https://gitlab.com/gitlab-org/security-products/gemnasium-db/blob/master/CONTRIBUTING.md).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 56a4cbd26d2..31f0b5a050c 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -1,10 +1,22 @@
+---
+type: reference, howto
+---
+
# GitLab Secure **(ULTIMATE)**
-Check your application for security vulnerabilities that may lead to unauthorized access,
-data leaks, and denial of services. GitLab will perform static and dynamic tests on the
-code of your application, looking for known flaws and report them in the merge request
-so you can fix them before merging. Security teams can use dashboards to get a
-high-level view on projects and groups, and start remediation processes when needed.
+Check your application for security vulnerabilities that may lead to
+unauthorized access, data leaks, and denial of services.
+
+GitLab will perform static and dynamic tests on the code of your application,
+looking for known flaws and report them in the merge request so you can fix
+them before merging.
+
+Security teams can use dashboards to get a high-level view on projects and
+groups, and start remediation processes when needed.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview of application security with GitLab, see
+[Security Deep Dive](https://www.youtube.com/watch?v=k4vEJnGYy84).
## Security scanning tools
@@ -54,7 +66,7 @@ Each security vulnerability in the merge request report or the
entry, a detailed information will pop up with different possible options:
- [Dismiss vulnerability](#dismissing-a-vulnerability): Dismissing a vulnerability
- will place a <s>strikethrough</s> styling on it.
+ will place a ~~strikethrough~~ styling on it.
- [Create issue](#creating-an-issue-for-a-vulnerability): The new issue will
have the title and description pre-populated with the information from the
vulnerability report and will be created as [confidential](../project/issues/confidential_issues.md) by default.
@@ -124,7 +136,7 @@ generated by GitLab. To apply the fix:
#### Creating a merge request from a vulnerability
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9224) in
- [GitLab Ultimate](https://about.gitlab.com/pricing) 11.9.
+> [GitLab Ultimate](https://about.gitlab.com/pricing) 11.9.
In certain cases, GitLab will allow you to create a merge request that will
automatically remediate the vulnerability. Any vulnerability that has a
@@ -135,3 +147,15 @@ If this action is available there will be a **Create merge request** button in t
Clicking on this button will create a merge request to apply the solution onto the source branch.
![Create merge request from vulnerability](img/create_issue_with_list_hover.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/license_management/index.md b/doc/user/application_security/license_management/index.md
index b0eb753938b..c324848c703 100644
--- a/doc/user/application_security/license_management/index.md
+++ b/doc/user/application_security/license_management/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# License Management **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5483)
@@ -227,3 +231,15 @@ pipeline ID that has a `license_management` job to see the Licenses tab with the
licenses (if any).
![License Management Pipeline Tab](img/license_management_pipeline_tab.png)
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 7df86eedd18..aac881112ff 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# Static Application Security Testing (SAST) **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3775)
@@ -334,3 +338,15 @@ Once a vulnerability is found, you can interact with it. Read more on how to
For more information about the vulnerabilities database update, check the
[maintenance table](../index.md#maintenance-and-update-of-the-vulnerabilities-database).
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/application_security/security_dashboard/img/dashboard.png b/doc/user/application_security/security_dashboard/img/dashboard.png
deleted file mode 100644
index a75168b1ce4..00000000000
--- a/doc/user/application_security/security_dashboard/img/dashboard.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/group_security_dashboard.png b/doc/user/application_security/security_dashboard/img/group_security_dashboard.png
new file mode 100644
index 00000000000..40689861e2a
--- /dev/null
+++ b/doc/user/application_security/security_dashboard/img/group_security_dashboard.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/project_security_dashboard.png b/doc/user/application_security/security_dashboard/img/project_security_dashboard.png
index f0dad6c54d0..89b310895d3 100644
--- a/doc/user/application_security/security_dashboard/img/project_security_dashboard.png
+++ b/doc/user/application_security/security_dashboard/img/project_security_dashboard.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 2a2385c00ae..ac8c1ac0354 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -1,3 +1,7 @@
+---
+type: reference, howto
+---
+
# GitLab Security Dashboard **(ULTIMATE)**
The Security Dashboard is a good place to get an overview of all the security
@@ -16,9 +20,9 @@ To benefit from the Security Dashboard you must first configure one of the
The Security Dashboard supports the following reports:
- [Container Scanning](../container_scanning/index.md)
-- [DAST](../dast/index.md)
+- [Dynamic Application Security Testing](../dast/index.md)
- [Dependency Scanning](../dependency_scanning/index.md)
-- [SAST](../sast/index.md)
+- [Static Application Security Testing](../sast/index.md)
## Requirements
@@ -43,13 +47,13 @@ for your project. Use it to find and fix vulnerabilities affecting the
## Group Security Dashboard
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6709) in
- [GitLab Ultimate](https://about.gitlab.com/pricing) 11.5.
+> [GitLab Ultimate](https://about.gitlab.com/pricing) 11.5.
The group Security Dashboard gives an overview of the vulnerabilities of all the
projects in a group and its subgroups.
First, navigate to the Security Dashboard found under your group's
-**Overview > Security Dashboard**.
+**Security** tab.
Once you're on the dashboard, at the top you should see a series of filters for:
@@ -58,7 +62,7 @@ Once you're on the dashboard, at the top you should see a series of filters for:
- Report type
- Project
-![dashboard with action buttons and metrics](img/dashboard.png)
+![dashboard with action buttons and metrics](img/group_security_dashboard.png)
Selecting one or more filters will filter the results in this page.
The first section is an overview of all the vulnerabilities, grouped by severity.
@@ -102,3 +106,15 @@ That way, reports are created even if no code change happens.
When using [Auto DevOps](../../../topics/autodevops/index.md), use
[special environment variables](../../../topics/autodevops/index.md#environment-variables)
to configure daily security scans.
+
+<!-- ## Troubleshooting
+
+Include any troubleshooting steps that you can foresee. If you know beforehand what issues
+one might have when setting this up, or when something is changed, or on upgrading, it's
+important to describe those, too. Think of things that may go wrong and include them here.
+This is important to minimize requests for support, and to avoid doc comments with
+questions that you know someone might ask.
+
+Each scenario can be a third-level heading, e.g. `### Getting error message X`.
+If you have none to add when creating a doc, leave this section in place
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index d5c836554ce..e1a6f8fcf71 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -5,11 +5,12 @@ be added directly to your configured cluster. These applications are
needed for [Review Apps](../../ci/review_apps/index.md) and
[deployments](../../ci/environments.md) when using [Auto DevOps](../../topics/autodevops/index.md).
You can install them after you
-[create a cluster](../project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
+[create a cluster](../project/clusters/index.md#add-new-gke-cluster).
## Installing applications
Applications managed by GitLab will be installed onto the `gitlab-managed-apps` namespace.
+
This namespace:
- Is different from the namespace used for project deployments.
diff --git a/doc/user/group/saml_sso/img/scim_attribute_mapping.png b/doc/user/group/saml_sso/img/scim_attribute_mapping.png
index c9f6b71f5b0..dad459d8c28 100644
--- a/doc/user/group/saml_sso/img/scim_attribute_mapping.png
+++ b/doc/user/group/saml_sso/img/scim_attribute_mapping.png
Binary files differ
diff --git a/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png b/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png
new file mode 100644
index 00000000000..85e5648816e
--- /dev/null
+++ b/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png
Binary files differ
diff --git a/doc/user/group/saml_sso/img/scim_provisioning_status.png b/doc/user/group/saml_sso/img/scim_provisioning_status.png
new file mode 100644
index 00000000000..4b8887b5418
--- /dev/null
+++ b/doc/user/group/saml_sso/img/scim_provisioning_status.png
Binary files differ
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 55c5a18db7d..bc74725bbc9 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -45,7 +45,7 @@ The following identity providers are supported:
Feature.enable(:group_scim, group)
```
-### GitLab configuration
+## GitLab configuration
Once [Single sign-on](index.md) has been configured, we can:
@@ -55,41 +55,48 @@ Once [Single sign-on](index.md) has been configured, we can:
![SCIM token configuration](img/scim_token.png)
-## SCIM IdP configuration
+## Identity Provider configuration
-### Configuration on Azure
+### Azure
-In the [Single sign-on](index.md) configuration for the group, make sure
-that the **Name identifier value** (NameID) points to a unique identifier, such
-as the `user.objectid`. This will match the `extern_uid` used on GitLab.
+First, double check the [Single sign-on](index.md) configuration for your group and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab.
-The GitLab app in Azure needs to be configured following
-[Azure's SCIM setup](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#getting-started).
+![Name identifier value mapping](img/scim_name_identifier_mapping.png)
-Note the following:
+#### Set up admin credentials
+
+Next, configure your GitLab application in Azure by following the
+[Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim)
+section in Azure's SCIM setup documentation.
+
+During this configuration, note the following:
- The `Tenant URL` and `secret token` are the ones retrieved in the
[previous step](#gitlab-configuration).
- Should there be any problems with the availability of GitLab or similar
errors, the notification email set will get those.
+- It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox.
- For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled.
-You can then test the connection clicking on `Test Connection`.
+You can then test the connection by clicking on **Test Connection**. If the connection is successful, be sure to save your configuration before moving on.
-### Synchronize Azure Active Directory users
+#### Configure attribute mapping
-1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure
- the attribute mapping.
-1. Select the unique identifier (in the example `objectId`) as the `id` and `externalId`,
- and enable the `Create`, `Update`, and `Delete` actions.
-1. Map the `userPricipalName` to `emails[type eq "work"].value` and `mailNickname` to
- `userName`.
+1. Click on `Synchronize Azure Active Directory Users to AppName`, to configure the attribute mapping.
+1. Click **Delete** next to the `mail` mapping.
+1. Map `userPrincipalName` to `emails[type eq "work"].value` and change it's **Matching precedence** to `2`.
+1. Map `mailNickname` to `userName`.
+1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, **Target attribute** to `id`, **Match objects using this attribute** to `Yes`, and **Matching precedence** to `1`.
+1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, and **Target attribute** to `externalId`.
+1. Click the `userPrincipalName` mapping and change **Match objects using this attribute** to `No`.
- Example configuration:
+ Save your changes and you should have the following configuration:
![Azure's attribute mapping configuration](img/scim_attribute_mapping.png)
-1. Click on **Show advanced options > Edit attribute list for AppName**.
+ NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`.
+
+1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**.
1. Leave the `id` as the primary and only required field.
NOTE: **Note:**
@@ -99,12 +106,14 @@ You can then test the connection clicking on `Test Connection`.
![Azure's attribute advanced configuration](img/scim_advanced.png)
1. Save all the screens and, in the **Provisioning** step, set
- the `Provisioning Status` to `ON`.
+ the `Provisioning Status` to `On`.
+
+ ![Provisioning status toggle switch](img/scim_provisioning_status.png)
NOTE: **Note:**
You can control what is actually synced by selecting the `Scope`. For example,
`Sync only assigned users and groups` will only sync the users assigned to
- the application (`Users and groups`), otherwise it will sync the whole Active Directory.
+ the application (`Users and groups`), otherwise, it will sync the whole Active Directory.
Once enabled, the synchronization details and any errors will appear on the
bottom of the **Provisioning** screen, together with a link to the audit logs.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 37f3f21f539..9380dcf2a32 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -136,26 +136,26 @@ Supported formats (named colors are not supported):
Color written inside backticks will be followed by a color "chip":
```markdown
-`#F00`
-`#F00A`
-`#FF0000`
-`#FF0000AA`
-`RGB(0,255,0)`
-`RGB(0%,100%,0%)`
-`RGBA(0,255,0,0.3)`
-`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.3)`
-```
-
-`#F00`
-`#F00A`
-`#FF0000`
-`#FF0000AA`
-`RGB(0,255,0)`
-`RGB(0%,100%,0%)`
-`RGBA(0,255,0,0.3)`
-`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.3)`
+`#F00`
+`#F00A`
+`#FF0000`
+`#FF0000AA`
+`RGB(0,255,0)`
+`RGB(0%,100%,0%)`
+`RGBA(0,255,0,0.3)`
+`HSL(540,70%,50%)`
+`HSLA(540,70%,50%,0.3)`
+```
+
+`#F00`
+`#F00A`
+`#FF0000`
+`#FF0000AA`
+`RGB(0,255,0)`
+`RGB(0%,100%,0%)`
+`RGBA(0,255,0,0.3)`
+`HSL(540,70%,50%)`
+`HSLA(540,70%,50%,0.3)`
### Diagrams and flowcharts using Mermaid
@@ -397,6 +397,7 @@ unordered or ordered lists:
- [ ] Sub-task 1
- [x] Sub-task 2
- [ ] Sub-task 3
+
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
@@ -408,6 +409,7 @@ unordered or ordered lists:
- [ ] Sub-task 1
- [x] Sub-task 2
- [ ] Sub-task 3
+
1. [x] Completed task
1. [ ] Incomplete task
1. [ ] Sub-task 1
@@ -976,7 +978,7 @@ after the `</summary>` tag and before the `</details>` tag, as shown in the exam
These details _will_ remain **hidden** until expanded.
- PASTE LOGS HERE
+PASTE LOGS HERE
</details>
```
@@ -988,7 +990,7 @@ These details _will_ remain **hidden** until expanded.
These details <em>will</em> remain <b>hidden</b> until expanded.
- PASTE LOGS HERE
+PASTE LOGS HERE
</details>
@@ -1047,14 +1049,14 @@ A new line due to the previous backslash.
First paragraph.
Another line in the same paragraph.
-A third line in the same paragraph, but this time ending with two spaces.
+A third line in the same paragraph, but this time ending with two spaces.
A new line directly under the first paragraph.
<!-- (Do *NOT* remove the two ending whitespaces in the second line) -->
<!-- (They are needed for the Markdown text to render correctly on docs.gitlab.com, the backslash works fine inside GitLab itself) -->
Second paragraph.
-Another line, this time ending with a backslash.
+Another line, this time ending with a backslash.
A new line due to the previous backslash.
### Links
@@ -1135,13 +1137,13 @@ GFM will autolink almost any URL you put into your text:
### Lists
Ordered and unordered lists can be easily created. Add the number you want the list
-to start with, like `1. ` (with a space) at the start of each line for ordered lists.
+to start with, like `1.`, followed by a space, at the start of each line for ordered lists.
After the first number, it does not matter what number you use, ordered lists will be
-numbered automatically by vertical order, so repeating `1. ` for all items in the
-same list is common. If you start with a number other than `1. `, it will use that as the first
+numbered automatically by vertical order, so repeating `1.` for all items in the
+same list is common. If you start with a number other than `1.`, it will use that as the first
number, and count up from there.
-Add a `* `, `- ` or `+ ` (with a space) at the start of each line for unordered lists, but
+Add a `*`, `-` or `+`, followed by a space, at the start of each line for unordered lists, but
you should not use a mix of them.
Examples:
@@ -1156,7 +1158,9 @@ Examples:
4. And another item.
* Unordered lists can use asterisks
+
- Or minuses
+
+ Or pluses
```
@@ -1170,9 +1174,11 @@ Examples:
1. Next ordered sub-list item
1. And another item.
-* Unordered lists can use asterisks
+- Unordered lists can use asterisks
+
- Or minuses
-+ Or pluses
+
+- Or pluses
---
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 61a30a775b0..fc5eb8c7b56 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -147,39 +147,40 @@ You can also set your current status [using the API](../../api/users.md#user-sta
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21598) in GitLab 11.4.
-A commit email, is the email that will be displayed in every Git-related action done through the
-GitLab interface.
+A commit email is an email address displayed in every Git-related action carried out through the GitLab interface.
-You are able to select from the list of your own verified emails which email you want to use as the commit email.
+Any of your own verified email addresses can be used as the commit email.
-To change it:
+To change your commit email:
-1. Open the user menu in the top-right corner of the navigation bar.
-1. Hit **Commit email** selection box.
+1. Click on your avatar at the top-right corner of the navigation bar.
+1. From the menu that appears, click **Settings**.
+1. In the **Main settings** section, locate **Commit email** dropdown.
1. Select any of the verified emails.
-1. Hit **Update profile settings**.
+1. Press **Update profile settings**.
### Private commit email
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22560) in GitLab 11.5.
-GitLab provides the user with an automatically generated private commit email option,
+GitLab provides users with an automatically generated private commit email option,
which allows the user to not make their email information public.
To enable this option:
-1. Open the user menu in the top-right corner of the navigation bar.
-1. Hit **Commit email** selection box.
-1. Select **Use a private email** option.
-1. Hit **Update profile settings**.
+1. Click on your avatar at the top-right corner of the navigation bar.
+1. From the menu that appears, click **Settings**.
+1. In the **Main settings** section, locate **Commit email** dropdown.
+1. Select the "Use a private email" option.
+1. Press **Update profile settings**.
Once this option is enabled, every Git-related action will be performed using the private commit email.
-In order to stay fully annonymous, you can also copy this private commit email
+In order to stay fully anonymous, you can also copy this private commit email
and configure it on your local machine using the following command:
-```
-git config --global user.email "YOUR_PRIVATE_COMMIT_EMAIL"
+```sh
+git config --global user.email <YOUR_PRIVATE_COMMIT_EMAIL>
```
## Troubleshooting
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 35f2976899b..670dde6bb00 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -1,33 +1,111 @@
-# Connecting GitLab with a Kubernetes cluster
+# Kubernetes clusters
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35954) in GitLab 10.1.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35954) for
+> projects in GitLab 10.1.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/34758) for
+> [groups](../../group/clusters/index.md) in GitLab 11.6.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/39840) for
+> [instances](../../instance/clusters/index.md) in GitLab 11.11.
-Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes
-cluster in a few steps.
+GitLab provides many features with a Kubernetes integration. Kubernetes can be
+integrated with projects, but also:
+
+- [Groups](../../group/clusters/index.md).
+- [Instances](../../instance/clusters/index.md).
NOTE: **Scalable app deployment with GitLab and Google Cloud Platform**
[Watch the webcast](https://about.gitlab.com/webcast/scalable-app-deploy/) and learn how to spin up a Kubernetes cluster managed by Google Cloud Platform (GCP) in a few clicks.
## Overview
-With one or more Kubernetes clusters associated to your project, you can use
-[Review Apps](../../../ci/review_apps/index.md), deploy your applications, run
-your pipelines, use it with [Auto DevOps](../../../topics/autodevops/index.md),
-and much more, all from within GitLab.
+Using the GitLab project Kubernetes integration, you can:
+
+- Use [Review Apps](../../../ci/review_apps/index.md).
+- Run [pipelines](../../../ci/pipelines.md).
+- [Deploy](#deploying-to-a-kubernetes-cluster) your applications.
+- Detect and [monitor Kubernetes](#kubernetes-monitoring).
+- Use it with [Auto DevOps](#auto-devops).
+- Use [Web terminals](#web-terminals).
+- Use [Deploy Boards](#deploy-boards-premium). **(PREMIUM)**
+- Use [Canary Deployments](#canary-deployments-premium). **(PREMIUM)**
+- View [Pod logs](#pod-logs-ultimate). **(ULTIMATE)**
+
+You can also:
+
+- Connect and deploy to an [Amazon EKS cluster](eks_and_gitlab/index.html).
+- Run serverless workloads on [Kubernetes with Knative](serverless/index.md).
+
+### Deploy Boards **(PREMIUM)**
+
+GitLab's Deploy Boards offer a consolidated view of the current health and
+status of each CI [environment](../../../ci/environments.md) running on Kubernetes,
+displaying the status of the pods in the deployment. Developers and other
+teammates can view the progress and status of a rollout, pod by pod, in the
+workflow they already use without any need to access Kubernetes.
+
+[Read more about Deploy Boards](../deploy_boards.md)
+
+### Canary Deployments **(PREMIUM)**
+
+Leverage [Kubernetes' Canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments)
+and visualize your canary deployments right inside the Deploy Board, without
+the need to leave GitLab.
+
+[Read more about Canary Deployments](../canary_deployments.md)
+
+### Pod logs **(ULTIMATE)**
+
+GitLab makes it easy to view the logs of running pods in connected Kubernetes clusters. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface.
+
+[Read more about Kubernetes pod logs](kubernetes_pod_logs.md)
+
+### Kubernetes monitoring
-There are two options when adding a new cluster to your project; either associate
-your account with Google Kubernetes Engine (GKE) so that you can [create new
-clusters](#adding-and-creating-a-new-gke-cluster-via-gitlab) from within GitLab,
-or provide the credentials to an [existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster).
+Automatically detect and monitor Kubernetes metrics. Automatic monitoring of
+[NGINX ingress](../integrations/prometheus_library/nginx.md) is also supported.
+
+[Read more about Kubernetes monitoring](../integrations/prometheus_library/kubernetes.md)
+
+### Auto DevOps
+
+Auto DevOps automatically detects, builds, tests, deploys, and monitors your
+applications.
+
+To make full use of Auto DevOps(Auto Deploy, Auto Review Apps, and Auto Monitoring)
+you will need the Kubernetes project integration enabled.
+
+[Read more about Auto DevOps](../../../topics/autodevops/index.md)
+
+### Web terminals
NOTE: **Note:**
-From [GitLab 11.6](https://gitlab.com/gitlab-org/gitlab-ce/issues/34758) you
-can also associate a Kubernetes cluster to your groups and from
-[GitLab 11.11](https://gitlab.com/gitlab-org/gitlab-ce/issues/39840),
-to the GitLab instance. Learn more about [group-level](../../group/clusters/index.md)
-and [instance-level](../../instance/clusters/index.md) Kubernetes clusters.
+Introduced in GitLab 8.15. You must be the project owner or have `maintainer` permissions
+to use terminals. Support is limited to the first container in the
+first pod of your environment.
+
+When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
+support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
+Docker and Kubernetes, so you get a new shell session within your existing
+containers. To use this integration, you should deploy to Kubernetes using
+the deployment variables above, ensuring any deployments, replica sets, and
+pods are annotated with:
-## Adding and creating a new GKE cluster via GitLab
+- `app.gitlab.com/env: $CI_ENVIRONMENT_SLUG`
+- `app.gitlab.com/app: $CI_PROJECT_PATH_SLUG`
+
+`$CI_ENVIRONMENT_SLUG` and `$CI_PROJECT_PATH_SLUG` are the values of
+the CI variables.
+
+## Adding and removing clusters
+
+There are two options when adding a new cluster to your project:
+
+- Associate your account with Google Kubernetes Engine (GKE) to
+ [create new clusters](#add-new-gke-cluster) from within GitLab.
+- Provide credentials to an
+ [existing Kubernetes cluster](#add-existing-kubernetes-cluster).
+
+### Add new GKE cluster
TIP: **Tip:**
Every new Google Cloud Platform (GCP) account receives [$300 in credit upon sign up](https://console.cloud.google.com/freetrial),
@@ -39,7 +117,7 @@ The [Google authentication integration](../../../integration/google.md) must
be enabled in GitLab at the instance level. If that's not the case, ask your
GitLab administrator to enable it. On GitLab.com, this is enabled.
-### Requirements
+#### Requirements
Before creating your first cluster on Google Kubernetes Engine with GitLab's
integration, make sure the following requirements are met:
@@ -49,7 +127,7 @@ integration, make sure the following requirements are met:
- The Kubernetes Engine API and related service are enabled. It should work immediately but may take up to 10 minutes after you create a project. For more information see the
["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
-### Creating the cluster
+#### Creating the cluster
If all of the above requirements are met, you can proceed to create and add a
new Kubernetes cluster to your project:
@@ -57,7 +135,7 @@ new Kubernetes cluster to your project:
1. Navigate to your project's **Operations > Kubernetes** page.
NOTE: **Note:**
- You need Maintainer [permissions] and above to access the Kubernetes page.
+ You need Maintainer [permissions](../../permissions.md) and above to access the Kubernetes page.
1. Click **Add Kubernetes cluster**.
1. Click **Create with Google Kubernetes Engine**.
@@ -91,14 +169,14 @@ client certificate is enabled.
NOTE: **Note:**
Starting from [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/55902), all GKE clusters created by GitLab are RBAC enabled. Take a look at the [RBAC section](#rbac-cluster-resources) for more information.
-## Adding an existing Kubernetes cluster
+### Add existing Kubernetes cluster
To add an existing Kubernetes cluster to your project:
1. Navigate to your project's **Operations > Kubernetes** page.
NOTE: **Note:**
- You need Maintainer [permissions] and above to access the Kubernetes page.
+ You need Maintainer [permissions](../../permissions.md) and above to access the Kubernetes page.
1. Click **Add Kubernetes cluster**.
1. Click **Add an existing Kubernetes cluster** and fill in the details:
@@ -216,7 +294,36 @@ To add an existing Kubernetes cluster to your project:
After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](#installing-applications).
-## Security implications
+### Enabling or disabling integration
+
+After you have successfully added your cluster information, you can enable the
+Kubernetes cluster integration:
+
+1. Click the **Enabled/Disabled** switch
+1. Hit **Save** for the changes to take effect
+
+To disable the Kubernetes cluster integration, follow the same procedure.
+
+### Removing integration
+
+NOTE: **Note:**
+You need Maintainer [permissions](../../permissions.md) and above to remove a Kubernetes cluster integration.
+
+NOTE: **Note:**
+When you remove a cluster, you only remove its relation to GitLab, not the
+cluster itself. To remove the cluster, you can do so by visiting the GKE
+dashboard or using `kubectl`.
+
+To remove the Kubernetes cluster integration from your project, simply click the
+**Remove integration** button. You will then be able to follow the procedure
+and add a Kubernetes cluster again.
+
+## Cluster configuration
+
+This section covers important considerations for configuring Kubernetes
+clusters with GitLab.
+
+### Security implications
CAUTION: **Important:**
The whole cluster security is based on a model where [developers](../../permissions.md)
@@ -227,7 +334,7 @@ functionalities needed to successfully build and deploy a containerized
application. Bear in mind that the same credentials are used for all the
applications running on the cluster.
-## GitLab-managed clusters
+### GitLab-managed clusters
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011) in GitLab 11.5.
> Became [optional](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26565) in GitLab 11.11.
@@ -246,7 +353,7 @@ NOTE: **Note:**
If you [install applications](#installing-applications) on your cluster, GitLab will create
the resources required to run these even if you have chosen to manage your own cluster.
-## Base domain
+### Base domain
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24580) in GitLab 11.8.
@@ -264,7 +371,7 @@ you can either:
- Create an `A` record that points to the Ingress IP address with your domain provider.
- Enter a wildcard DNS address using a service such as nip.io or xip.io. For example, `192.168.1.1.xip.io`.
-## Access controls
+### Access controls
When creating a cluster in GitLab, you will be asked if you would like to create either:
@@ -294,12 +401,12 @@ Helm will also create additional service accounts and other resources for each
installed application. Consult the documentation of the Helm charts for each application
for details.
-If you are [adding an existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster),
+If you are [adding an existing Kubernetes cluster](#add-existing-kubernetes-cluster),
ensure the token of the account has administrator privileges for the cluster.
The resources created by GitLab differ depending on the type of cluster.
-### ABAC cluster resources
+#### ABAC cluster resources
GitLab creates the following resources for ABAC clusters.
@@ -312,7 +419,7 @@ GitLab creates the following resources for ABAC clusters.
| Project namespace | `ServiceAccount` | Uses namespace of Project | Deploying to a cluster |
| Project namespace | `Secret` | Token for project ServiceAccount | Deploying to a cluster |
-### RBAC cluster resources
+#### RBAC cluster resources
GitLab creates the following resources for RBAC clusters.
@@ -330,11 +437,12 @@ GitLab creates the following resources for RBAC clusters.
NOTE: **Note:**
Project-specific resources are only created if your cluster is [managed by GitLab](#gitlab-managed-clusters).
-### Security of GitLab Runners
+#### Security of GitLab Runners
GitLab Runners have the [privileged mode](https://docs.gitlab.com/runner/executors/docker.html#the-privileged-mode)
enabled by default, which allows them to execute special commands and running
-Docker in Docker. This functionality is needed to run some of the [Auto DevOps]
+Docker in Docker. This functionality is needed to run some of the
+[Auto DevOps](../../../topics/autodevops/index.md)
jobs. This implies the containers are running in privileged mode and you should,
therefore, be aware of some important details.
@@ -353,6 +461,69 @@ If you don't want to use GitLab Runner in privileged mode, either:
1. Installing a Runner
[using `docker+machine`](https://docs.gitlab.com/runner/executors/docker_machine.html).
+### Setting the environment scope **(PREMIUM)**
+
+When adding more than one Kubernetes cluster to your project, you need to differentiate
+them with an environment scope. The environment scope associates clusters with [environments](../../../ci/environments.md) similar to how the
+[environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) work.
+
+The default environment scope is `*`, which means all jobs, regardless of their
+environment, will use that cluster. Each scope can only be used by a single
+cluster in a project, and a validation error will occur if otherwise.
+Also, jobs that don't have an environment keyword set will not be able to access any cluster.
+
+For example, let's say the following Kubernetes clusters exist in a project:
+
+| Cluster | Environment scope |
+| ----------- | ----------------- |
+| Development | `*` |
+| Staging | `staging` |
+| Production | `production` |
+
+And the following environments are set in [`.gitlab-ci.yml`](../../../ci/yaml/README.md):
+
+```yaml
+stages:
+- test
+- deploy
+
+test:
+ stage: test
+ script: sh test
+
+deploy to staging:
+ stage: deploy
+ script: make deploy
+ environment:
+ name: staging
+ url: https://staging.example.com/
+
+deploy to production:
+ stage: deploy
+ script: make deploy
+ environment:
+ name: production
+ url: https://example.com/
+```
+
+The result will then be:
+
+- The development cluster will be used for the "test" job.
+- The staging cluster will be used for the "deploy to staging" job.
+- The production cluster will be used for the "deploy to production" job.
+
+### Multiple Kubernetes clusters **(PREMIUM)**
+
+> Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3.
+
+With GitLab Premium, you can associate more than one Kubernetes cluster to your
+project. That way you can have different clusters for different environments,
+like dev, staging, production, etc.
+
+Simply add another cluster, like you did the first time, and make sure to
+[set an environment scope](#setting-the-environment-scope-premium) that will
+differentiate the new cluster with the rest.
+
## Installing applications
GitLab can install and manage some applications in your project-level
@@ -360,7 +531,7 @@ cluster. For more information on installing, upgrading, uninstalling,
and troubleshooting applications for your project cluster, see
[Gitlab Managed Apps](../../clusters/applications.md).
-## Getting the external endpoint
+### Getting the external endpoint
NOTE: **Note:**
With the following procedure, a load balancer must be installed in your cluster
@@ -371,7 +542,7 @@ to obtain the endpoint. You can use either
In order to publish your web application, you first need to find the endpoint which will be either an IP
address or a hostname associated with your load balancer.
-### Automatically determining the external endpoint
+#### Automatically determining the external endpoint
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6.
@@ -386,7 +557,7 @@ and your cluster runs on Google Kubernetes Engine:
If GitLab is still unable to determine the endpoint of your Ingress or Knative application, you can
manually determine it by following the steps below.
-### Manually determining the external endpoint
+#### Manually determining the external endpoint
If the cluster is on GKE, click the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the
@@ -425,7 +596,7 @@ Otherwise, you can list the IP addresses of all load balancers:
kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
```
-### Using a static IP
+#### Using a static IP
By default, an ephemeral external IP address is associated to the cluster's load
balancer. If you associate the ephemeral IP with your DNS and the IP changes,
@@ -435,79 +606,19 @@ reserved IP.
Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip).
-### Pointing your DNS at the external endpoint
+#### Pointing your DNS at the external endpoint
Once you've set up the external endpoint, you should associate it with a [wildcard DNS
record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) such as `*.example.com.`
in order to be able to reach your apps. If your external endpoint is an IP address,
use an A record. If your external endpoint is a hostname, use a CNAME record.
-## Multiple Kubernetes clusters **(PREMIUM)**
+## Deploying to a Kubernetes cluster
-> Introduced in [GitLab Premium][ee] 10.3.
+A Kubernetes cluster can be the destination for a deployment job, using
+special [deployment variables](#deployment-variables).
-With GitLab Premium, you can associate more than one Kubernetes clusters to your
-project. That way you can have different clusters for different environments,
-like dev, staging, production, etc.
-
-Simply add another cluster, like you did the first time, and make sure to
-[set an environment scope](#setting-the-environment-scope-premium) that will
-differentiate the new cluster with the rest.
-
-## Setting the environment scope **(PREMIUM)**
-
-When adding more than one Kubernetes cluster to your project, you need to differentiate
-them with an environment scope. The environment scope associates clusters with [environments](../../../ci/environments.md) similar to how the
-[environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) work.
-
-The default environment scope is `*`, which means all jobs, regardless of their
-environment, will use that cluster. Each scope can only be used by a single
-cluster in a project, and a validation error will occur if otherwise.
-Also, jobs that don't have an environment keyword set will not be able to access any cluster.
-
----
-
-For example, let's say the following Kubernetes clusters exist in a project:
-
-| Cluster | Environment scope |
-| ----------- | ----------------- |
-| Development | `*` |
-| Staging | `staging` |
-| Production | `production` |
-
-And the following environments are set in [`.gitlab-ci.yml`](../../../ci/yaml/README.md):
-
-```yaml
-stages:
-- test
-- deploy
-
-test:
- stage: test
- script: sh test
-
-deploy to staging:
- stage: deploy
- script: make deploy
- environment:
- name: staging
- url: https://staging.example.com/
-
-deploy to production:
- stage: deploy
- script: make deploy
- environment:
- name: production
- url: https://example.com/
-```
-
-The result will then be:
-
-- The development cluster will be used for the "test" job.
-- The staging cluster will be used for the "deploy to staging" job.
-- The production cluster will be used for the "deploy to production" job.
-
-## Deployment variables
+### Deployment variables
The Kubernetes cluster integration exposes the following
[deployment variables](../../../ci/variables/README.md#deployment-environment-variables) in the
@@ -527,7 +638,7 @@ NOTE: **NOTE:**
Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
service account of the cluster integration.
-### Troubleshooting failed deployment jobs
+### Troubleshooting
Before the deployment jobs starts, GitLab creates the following specifically for
the deployment job:
@@ -559,105 +670,8 @@ namespaces and service accounts yourself.
## Monitoring your Kubernetes cluster **(ULTIMATE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4701) in [GitLab Ultimate][ee] 10.6.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4701) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
When [Prometheus is deployed](#installing-applications), GitLab will automatically monitor the cluster's health. At the top of the cluster settings page, CPU and Memory utilization is displayed, along with the total amount available. Keeping an eye on cluster resources can be important, if the cluster runs out of memory pods may be shutdown or fail to start.
![Cluster Monitoring](img/k8s_cluster_monitoring.png)
-
-## Enabling or disabling the Kubernetes cluster integration
-
-After you have successfully added your cluster information, you can enable the
-Kubernetes cluster integration:
-
-1. Click the **Enabled/Disabled** switch
-1. Hit **Save** for the changes to take effect
-
-You can now start using your Kubernetes cluster for your deployments.
-
-To disable the Kubernetes cluster integration, follow the same procedure.
-
-## Removing the Kubernetes cluster integration
-
-NOTE: **Note:**
-You need Maintainer [permissions] and above to remove a Kubernetes cluster integration.
-
-NOTE: **Note:**
-When you remove a cluster, you only remove its relation to GitLab, not the
-cluster itself. To remove the cluster, you can do so by visiting the GKE
-dashboard or using `kubectl`.
-
-To remove the Kubernetes cluster integration from your project, simply click the
-**Remove integration** button. You will then be able to follow the procedure
-and add a Kubernetes cluster again.
-
-## What you can get with the Kubernetes integration
-
-Here's what you can do with GitLab if you enable the Kubernetes integration.
-
-### Deploy Boards **(PREMIUM)**
-
-GitLab's Deploy Boards offer a consolidated view of the current health and
-status of each CI [environment](../../../ci/environments.md) running on Kubernetes,
-displaying the status of the pods in the deployment. Developers and other
-teammates can view the progress and status of a rollout, pod by pod, in the
-workflow they already use without any need to access Kubernetes.
-
-[Read more about Deploy Boards](../deploy_boards.md)
-
-### Canary Deployments **(PREMIUM)**
-
-Leverage [Kubernetes' Canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments)
-and visualize your canary deployments right inside the Deploy Board, without
-the need to leave GitLab.
-
-[Read more about Canary Deployments](../canary_deployments.md)
-
-### Pod logs **(ULTIMATE)**
-
-GitLab makes it easy to view the logs of running pods in connected Kubernetes clusters. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface.
-
-[Read more about Kubernetes pod logs](kubernetes_pod_logs.md)
-
-### Kubernetes monitoring
-
-Automatically detect and monitor Kubernetes metrics. Automatic monitoring of
-[NGINX ingress](../integrations/prometheus_library/nginx.md) is also supported.
-
-[Read more about Kubernetes monitoring](../integrations/prometheus_library/kubernetes.md)
-
-### Auto DevOps
-
-Auto DevOps automatically detects, builds, tests, deploys, and monitors your
-applications.
-
-To make full use of Auto DevOps(Auto Deploy, Auto Review Apps, and Auto Monitoring)
-you will need the Kubernetes project integration enabled.
-
-[Read more about Auto DevOps](../../../topics/autodevops/index.md)
-
-### Web terminals
-
-NOTE: **Note:**
-Introduced in GitLab 8.15. You must be the project owner or have `maintainer` permissions
-to use terminals. Support is limited to the first container in the
-first pod of your environment.
-
-When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
-support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
-Docker and Kubernetes, so you get a new shell session within your existing
-containers. To use this integration, you should deploy to Kubernetes using
-the deployment variables above, ensuring any pods you create are labelled with
-`app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest!
-
-### Integrating Amazon EKS cluster with GitLab
-
-- Learn how to [connect and deploy to an Amazon EKS cluster](eks_and_gitlab/index.md).
-
-### Serverless
-
-- [Run serverless workloads on Kubernetes with Knative.](serverless/index.md)
-
-[permissions]: ../../permissions.md
-[ee]: https://about.gitlab.com/pricing/
-[Auto DevOps]: ../../../topics/autodevops/index.md
diff --git a/doc/user/project/clusters/runbooks/index.md b/doc/user/project/clusters/runbooks/index.md
index c021d059d30..8df27976662 100644
--- a/doc/user/project/clusters/runbooks/index.md
+++ b/doc/user/project/clusters/runbooks/index.md
@@ -35,7 +35,7 @@ for an overview of how this is accomplished in GitLab!**
To create an executable runbook, you will need:
1. **Kubernetes** - A Kubernetes cluster is required to deploy the rest of the applications.
- The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
+ The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#add-new-gke-cluster).
1. **Helm Tiller** - Helm is a package manager for Kubernetes and is required to install
all the other applications. It is installed in its own pod inside the cluster which
can run the helm CLI in a safe environment.
@@ -60,7 +60,7 @@ the components outlined above and the preloaded demo runbook.
### 1. Add a Kubernetes cluster
-Follow the steps outlined in [Adding and creating a new GKE cluster via GitLab](../index.md#adding-and-creating-a-new-gke-cluster-via-gitlab)
+Follow the steps outlined in [Add new GKE cluster](../index.md#add-new-gke-cluster)
to add a Kubernetes cluster to your project.
### 2. Install Helm Tiller, Ingress, and JupyterHub
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index a32759c7bdc..92ad49e9448 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -28,7 +28,7 @@ To run Knative on Gitlab, you will need:
- If you are planning on deploying a serverless application, clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
- The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
+ The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#add-new-gke-cluster).
The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
Knative.
@@ -96,7 +96,8 @@ cluster which already has Knative installed.
You must do the following:
1. Follow the steps to
- [add an existing Kubernetes cluster](../index.md#adding-an-existing-kubernetes-cluster).
+ [add an existing Kubernetes
+ cluster](../index.md#add-existing-kubernetes-cluster).
1. Ensure GitLab can manage Knative:
- For a non-GitLab managed cluster, ensure that the service account for the token
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 9368c56004d..c9eb81b990c 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -1,13 +1,8 @@
# GitLab Container Registry
-> **Notes:**
->
> - [Introduced][ce-4040] in GitLab 8.8.
> - Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
> versions earlier than 1.10.
-> - This document is about the user guide. To learn how to enable GitLab Container
-> Registry across your GitLab instance, visit the
-> [administrator documentation](../../administration/container_registry.md).
> - Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
> to pass a [personal access token][pat] instead of your password in order to
> login to GitLab's Container Registry.
@@ -16,28 +11,33 @@
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
+This document is the user guide. To learn how to enable GitLab Container
+Registry across your GitLab instance, visit the
+[administrator documentation](../../administration/container_registry.md).
+
You can read more about Docker Registry at <https://docs.docker.com/registry/introduction/>.
## Enable the Container Registry for your project
-NOTE: **Note:**
-If you cannot find the Container Registry entry under your project's settings,
-that means that it is not enabled in your GitLab instance. Ask your administrator
-to enable it.
-
-1. First, ask your system administrator to enable GitLab Container Registry
- following the [administration documentation](../../administration/container_registry.md).
- If you are using GitLab.com, this is enabled by default so you can start using
- the Registry immediately. Currently there is a soft (10GB) size restriction for
- registry on GitLab.com, as part of the [repository size limit](repository/index.md).
-1. Go to your [project's General settings](settings/index.md#sharing-and-permissions)
+If you cannot find the **Packages > Container Registry** entry under your
+project's sidebar, it is not enabled in your GitLab instance. Ask your
+administrator to enable GitLab Container Registry following the
+[administration documentation](../../administration/container_registry.md).
+
+If you are using GitLab.com, this is enabled by default so you can start using
+the Registry immediately. Currently there is a soft (10GB) size restriction for
+registry on GitLab.com, as part of the [repository size limit](repository/index.md).
+
+Once enabled for your GitLab instance, to enable Container Registry for your
+project:
+
+1. Go to your project's **Settings > General** page.
+1. Expand the **Visibility, project features, permissions** section
and enable the **Container Registry** feature on your project. For new
projects this might be enabled by default. For existing projects
(prior GitLab 8.8), you will have to explicitly enable it.
-1. Hit **Save changes** for the changes to take effect. You should now be able
- to see the **Registry** link in the sidebar.
-
-![Container Registry](img/container_registry.png)
+1. Press **Save changes** for the changes to take effect. You should now be able
+ to see the **Packages > Container Registry** link in the sidebar.
## Build and push images
@@ -49,14 +49,14 @@ to enable it.
> - To move or rename a repository with a container registry you will have to
> delete all existing images.
-If you visit the **Registry** link under your project's menu, you can see the
-explicit instructions to login to the Container Registry using your GitLab
-credentials.
+If you visit the **Packages > Container Registry** link under your project's
+menu, you can see the explicit instructions to login to the Container Registry
+using your GitLab credentials.
For example if the Registry's URL is `registry.example.com`, then you should be
able to login with:
-```
+```sh
docker login registry.example.com
```
@@ -64,14 +64,14 @@ Building and publishing images should be a straightforward process. Just make
sure that you are using the Registry URL with the namespace and project name
that is hosted on GitLab:
-```
+```sh
docker build -t registry.example.com/group/project/image .
docker push registry.example.com/group/project/image
```
Your image will be named after the following scheme:
-```
+```text
<registry URL>/<namespace>/<project>/<image>
```
@@ -79,7 +79,7 @@ GitLab supports up to three levels of image repository names.
Following examples of image tags are valid:
-```
+```text
registry.example.com/group/project:some-tag
registry.example.com/group/project/image:latest
registry.example.com/group/project/my/image:rc1
@@ -90,7 +90,7 @@ registry.example.com/group/project/my/image:rc1
To download and run a container from images hosted in GitLab Container Registry,
use `docker run`:
-```
+```sh
docker run [options] registry.example.com/group/project/image [arguments]
```
@@ -100,7 +100,7 @@ For more information on running Docker containers, visit the
## Control Container Registry from within GitLab
GitLab offers a simple Container Registry management panel. Go to your project
-and click **Registry** in the project menu.
+and click **Packages > Container Registry** in the project menu.
This view will show you all tags in your project and will easily allow you to
delete them.
@@ -173,9 +173,9 @@ curl localhost:5001/debug/vars
A Docker connection error can occur when there are special characters in either the group,
project or branch name. Special characters can include:
-* Leading underscore
-* Trailing hyphen/dash
-* Double hyphen/dash
+- Leading underscore
+- Trailing hyphen/dash
+- Double hyphen/dash
To get around this, you can [change the group path](../group/index.md#changing-a-groups-path),
[change the project path](../project/settings/index.md#renaming-a-repository) or chanage the branch
@@ -183,7 +183,8 @@ name.
### Advanced Troubleshooting
->**NOTE:** The following section is only recommended for experts.
+NOTE: **Note:**
+The following section is only recommended for experts.
Sometimes it's not obvious what is wrong, and you may need to dive deeper into
the communication between the Docker client and the Registry to find out
@@ -195,7 +196,7 @@ diagnose a problem with the S3 setup.
A user attempted to enable an S3-backed Registry. The `docker login` step went
fine. However, when pushing an image, the output showed:
-```
+```text
The push refers to a repository [s3-testing.myregistry.com:4567/root/docker-test/docker-image]
dc5e59c14160: Pushing [==================================================>] 14.85 kB
03c20c1a019a: Pushing [==================================================>] 2.048 kB
@@ -218,7 +219,7 @@ at the communication between the client and the Registry.
The REST API between the Docker client and Registry is [described
here](https://docs.docker.com/registry/spec/api/). Normally, one would just
use Wireshark or tcpdump to capture the traffic and see where things went
-wrong. However, since all communications between Docker clients and servers
+wrong. However, since all communications between Docker clients and servers
are done over HTTPS, it's a bit difficult to decrypt the traffic quickly even
if you know the private key. What can we do instead?
diff --git a/doc/user/project/img/container_registry.png b/doc/user/project/img/container_registry.png
deleted file mode 100644
index abbaf838538..00000000000
--- a/doc/user/project/img/container_registry.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 61c30e0b0ef..e609fe43507 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -323,8 +323,11 @@ Once enabled, an issue will be opened automatically when an alert is triggered w
- `starts_at`: Alert start time via `startsAt`
- `full_query`: Alert query extracted from `generatorURL`
- Optional list of attached annotations extracted from `annotations/*`
+- Alert [GFM](../../markdown.md): GitLab Flavored Markdown from `annotations/gitlab_incident_markdown`
-To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template.
+To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template, which will apply to all incidents. To limit quick actions or other information to only specific types of alerts, use the `annotations/gitlab_incident_markdown` field.
+
+Since [version 12.2](https://gitlab.com/gitlab-org/gitlab-ce/issues/63373), GitLab will tag each incident issue with the `incident` label automatically. If the label does not yet exist, it will be created automatically as well.
If the metric exceeds the threshold of the alert for over 5 minutes, an email will be sent to all [Maintainers and Owners](../../permissions.md#project-members-permissions) of the project.
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index d5ca853eff5..f78ec9d96e6 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -343,6 +343,30 @@ git push -o merge_request.remove_source_branch
You can also use this push option in addition to the
`merge_request.create` push option.
+### Set merge request title using git push options
+
+To set the title of an existing merge request, use
+the `merge_request.title` push option:
+
+```sh
+git push -o merge_request.title="The title I want"
+```
+
+You can also use this push option in addition to the
+`merge_request.create` push option.
+
+### Set merge request description using git push options
+
+To set the description of an existing merge request, use
+the `merge_request.description` push option:
+
+```sh
+git push -o merge_request.description="The description I want"
+```
+
+You can also use this push option in addition to the
+`merge_request.create` push option.
+
## Find the merge request that introduced a change
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383) in GitLab 10.5.
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 20a03dff2da..9a3c02e1f50 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -180,6 +180,5 @@ for details about the pipelines security model.
[ce-4892]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4892 "Allow developers to merge into a protected branch without having push access"
[ce-5081]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5081 "Allow creating protected branches that can't be pushed to"
[ce-21393]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21393
-[ee-restrict]: https://docs.gitlab.com/ee/user/project/protected_branches.html#restricting-push-and-merge-access-to-certain-users
[perm]: ../permissions.md
[ee]: https://about.gitlab.com/pricing/
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 10b4f8934d7..2e78331df6c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -366,10 +366,7 @@ module API
end
expose :request_access_enabled
expose :full_name, :full_path
-
- if ::Group.supports_nested_objects?
- expose :parent_id
- end
+ expose :parent_id
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index ec1020c7c78..f545f33c06b 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -114,10 +114,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
-
- if ::Group.supports_nested_objects?
- optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
- end
+ optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
use :optional_params
end
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index e7fed55170e..b35aa952f81 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -27,7 +27,7 @@ module API
requirements: { ref_name: /.+/ } do
authorize_download_artifacts!
- latest_build = user_project.latest_successful_build_for!(params[:job], params[:ref_name])
+ latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
present_carrierwave_file!(latest_build.artifacts_file)
end
@@ -45,7 +45,7 @@ module API
requirements: { ref_name: /.+/ } do
authorize_download_artifacts!
- build = user_project.latest_successful_build_for!(params[:job], params[:ref_name])
+ build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
path = Gitlab::Ci::Build::Artifacts::Path
.new(params[:artifact_path])
diff --git a/lib/api/validations/types/labels_list.rb b/lib/api/validations/types/labels_list.rb
index 47cd83c29cf..60277b99106 100644
--- a/lib/api/validations/types/labels_list.rb
+++ b/lib/api/validations/types/labels_list.rb
@@ -10,7 +10,7 @@ module API
when String
value.split(',').map(&:strip)
when Array
- value.map { |v| v.to_s.split(',').map(&:strip) }.flatten
+ value.flat_map { |v| v.to_s.split(',').map(&:strip) }
when LabelsList
value
else
diff --git a/lib/banzai/reference_redactor.rb b/lib/banzai/reference_redactor.rb
index eb5c35da375..936436982e7 100644
--- a/lib/banzai/reference_redactor.rb
+++ b/lib/banzai/reference_redactor.rb
@@ -33,7 +33,7 @@ module Banzai
#
# data - An Array of a Hashes mapping an HTML document to nodes to redact.
def redact_document_nodes(all_document_nodes)
- all_nodes = all_document_nodes.map { |x| x[:nodes] }.flatten
+ all_nodes = all_document_nodes.flat_map { |x| x[:nodes] }
visible = nodes_visible_to_user(all_nodes)
metadata = []
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
index c80f49f5ae0..c3a19af7a94 100644
--- a/lib/container_registry/client.rb
+++ b/lib/container_registry/client.rb
@@ -7,7 +7,9 @@ module ContainerRegistry
class Client
attr_accessor :uri
- MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json'.freeze
+ DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE = 'application/vnd.docker.distribution.manifest.v2+json'
+ OCI_MANIFEST_V1_TYPE = 'application/vnd.oci.image.manifest.v1+json'
+ ACCEPTED_TYPES = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE].freeze
# Taken from: FaradayMiddleware::FollowRedirects
REDIRECT_CODES = Set.new [301, 302, 303, 307]
@@ -60,12 +62,13 @@ module ContainerRegistry
end
def accept_manifest(conn)
- conn.headers['Accept'] = MANIFEST_VERSION
+ conn.headers['Accept'] = ACCEPTED_TYPES
conn.response :json, content_type: 'application/json'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
- conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v2+json'
+ conn.response :json, content_type: DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE
+ conn.response :json, content_type: OCI_MANIFEST_V1_TYPE
end
def response_body(response, allow_redirect: false)
diff --git a/lib/gitlab/auth/activity.rb b/lib/gitlab/auth/activity.rb
index 558628b5422..988ff196193 100644
--- a/lib/gitlab/auth/activity.rb
+++ b/lib/gitlab/auth/activity.rb
@@ -37,14 +37,17 @@ module Gitlab
def user_authenticated!
self.class.user_authenticated_counter_increment!
+
+ case @opts[:message]
+ when :two_factor_authenticated
+ self.class.user_two_factor_authenticated_counter_increment!
+ end
end
def user_session_override!
self.class.user_session_override_counter_increment!
case @opts[:message]
- when :two_factor_authenticated
- self.class.user_two_factor_authenticated_counter_increment!
when :sessionless_sign_in
self.class.user_sessionless_authentication_counter_increment!
end
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index 72a187377d0..91b9ddc0d00 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -60,8 +60,7 @@ module Gitlab
def get_info(key)
value = info[key]
- Gitlab::Utils.force_utf8(value) if value
- value
+ value.is_a?(String) ? Gitlab::Utils.force_utf8(value) : value
end
def username_and_email
diff --git a/lib/gitlab/background_migration/backfill_project_repositories.rb b/lib/gitlab/background_migration/backfill_project_repositories.rb
index c8d83cc1803..1d9aa050041 100644
--- a/lib/gitlab/background_migration/backfill_project_repositories.rb
+++ b/lib/gitlab/background_migration/backfill_project_repositories.rb
@@ -40,7 +40,7 @@ module Gitlab
end
def reload!
- @shards = Hash[*Shard.all.map { |shard| [shard.name, shard.id] }.flatten]
+ @shards = Hash[*Shard.all.flat_map { |shard| [shard.name, shard.id] }]
end
end
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index 7d40b459c9a..806c3a7a369 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -55,7 +55,7 @@ module Gitlab
def ensure_temporary_tracking_table_exists
table_name = :untracked_files_for_uploads
- unless ActiveRecord::Base.connection.data_source_exists?(table_name)
+ unless ActiveRecord::Base.connection.table_exists?(table_name)
UntrackedFile.connection.create_table table_name do |t|
t.string :path, limit: 600, null: false
t.index :path, unique: true
diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb
index 7f7cc62c8ef..15cccc6f287 100644
--- a/lib/gitlab/badge/coverage/report.rb
+++ b/lib/gitlab/badge/coverage/report.rb
@@ -14,7 +14,7 @@ module Gitlab
@ref = ref
@job = job
- @pipeline = @project.ci_pipelines.latest_successful_for(@ref)
+ @pipeline = @project.ci_pipelines.latest_successful_for_ref(@ref)
end
def entity
diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb
index 191f5d09645..99356226ef9 100644
--- a/lib/gitlab/ci/config/normalizer.rb
+++ b/lib/gitlab/ci/config/normalizer.rb
@@ -46,7 +46,7 @@ module Gitlab
parallelized_job_names = @parallelized_jobs.keys.map(&:to_s)
parallelized_config.each_with_object({}) do |(job_name, config), hash|
if config[:dependencies] && (intersection = config[:dependencies] & parallelized_job_names).any?
- parallelized_deps = intersection.map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }.flatten
+ parallelized_deps = intersection.flat_map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }
deps = config[:dependencies] - intersection + parallelized_deps
hash[job_name] = config.merge(dependencies: deps)
else
diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb
index 3446644eff8..2a0bf060c9b 100644
--- a/lib/gitlab/ci/status/factory.rb
+++ b/lib/gitlab/ci/status/factory.rb
@@ -34,11 +34,9 @@ module Gitlab
def extended_statuses
return @extended_statuses if defined?(@extended_statuses)
- groups = self.class.extended_statuses.map do |group|
+ @extended_statuses = self.class.extended_statuses.flat_map do |group|
Array(group).find { |status| status.matches?(@subject, @user) }
- end
-
- @extended_statuses = groups.flatten.compact
+ end.compact
end
def self.extended_statuses
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 7b9a169a91f..5c1c0c142e5 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -50,9 +50,6 @@ variables:
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
POSTGRES_VERSION: 9.6.2
- KUBERNETES_VERSION: 1.11.10
- HELM_VERSION: 2.14.0
-
DOCKER_DRIVER: overlay2
ROLLOUT_RESOURCE_TYPE: deployment
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index 6ead127e7b6..a8ec2d4781d 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -1,14 +1,17 @@
+.auto-deploy:
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.1.0"
+
review:
+ extends: .auto-deploy
stage: review
script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - initialize_tiller
- - create_secret
- - deploy
- - persist_environment_url
+ - auto-deploy check_kube_domain
+ - auto-deploy download_chart
+ - auto-deploy ensure_namespace
+ - auto-deploy initialize_tiller
+ - auto-deploy create_secret
+ - auto-deploy deploy
+ - auto-deploy persist_environment_url
environment:
name: review/$CI_COMMIT_REF_NAME
url: http://$CI_PROJECT_ID-$CI_ENVIRONMENT_SLUG.$KUBE_INGRESS_BASE_DOMAIN
@@ -27,13 +30,13 @@ review:
- $REVIEW_DISABLED
stop_review:
+ extends: .auto-deploy
stage: cleanup
variables:
GIT_STRATEGY: none
script:
- - install_dependencies
- - initialize_tiller
- - delete
+ - auto-deploy initialize_tiller
+ - auto-deploy delete
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
@@ -57,15 +60,15 @@ stop_review:
# STAGING_ENABLED.
staging:
+ extends: .auto-deploy
stage: staging
script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - initialize_tiller
- - create_secret
- - deploy
+ - auto-deploy check_kube_domain
+ - auto-deploy download_chart
+ - auto-deploy ensure_namespace
+ - auto-deploy initialize_tiller
+ - auto-deploy create_secret
+ - auto-deploy deploy
environment:
name: staging
url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_INGRESS_BASE_DOMAIN
@@ -81,15 +84,15 @@ staging:
# CANARY_ENABLED.
canary:
+ extends: .auto-deploy
stage: canary
script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - initialize_tiller
- - create_secret
- - deploy canary
+ - auto-deploy check_kube_domain
+ - auto-deploy download_chart
+ - auto-deploy ensure_namespace
+ - auto-deploy initialize_tiller
+ - auto-deploy create_secret
+ - auto-deploy deploy canary
environment:
name: production
url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
@@ -102,18 +105,18 @@ canary:
- $CANARY_ENABLED
.production: &production_template
+ extends: .auto-deploy
stage: production
script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - initialize_tiller
- - create_secret
- - deploy
- - delete canary
- - delete rollout
- - persist_environment_url
+ - auto-deploy check_kube_domain
+ - auto-deploy download_chart
+ - auto-deploy ensure_namespace
+ - auto-deploy initialize_tiller
+ - auto-deploy create_secret
+ - auto-deploy deploy
+ - auto-deploy delete canary
+ - auto-deploy delete rollout
+ - auto-deploy persist_environment_url
environment:
name: production
url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
@@ -152,17 +155,17 @@ production_manual:
# This job implements incremental rollout on for every push to `master`.
.rollout: &rollout_template
+ extends: .auto-deploy
script:
- - check_kube_domain
- - install_dependencies
- - download_chart
- - ensure_namespace
- - initialize_tiller
- - create_secret
- - deploy rollout $ROLLOUT_PERCENTAGE
- - scale stable $((100-ROLLOUT_PERCENTAGE))
- - delete canary
- - persist_environment_url
+ - auto-deploy check_kube_domain
+ - auto-deploy download_chart
+ - auto-deploy ensure_namespace
+ - auto-deploy initialize_tiller
+ - auto-deploy create_secret
+ - auto-deploy deploy rollout $ROLLOUT_PERCENTAGE
+ - auto-deploy scale stable $((100-ROLLOUT_PERCENTAGE))
+ - auto-deploy delete canary
+ - auto-deploy persist_environment_url
environment:
name: production
url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
@@ -240,331 +243,3 @@ rollout 100%:
<<: *manual_rollout_template
<<: *production_template
allow_failure: false
-
-.deploy_helpers: &deploy_helpers |
- [[ "$TRACE" ]] && set -x
- export RELEASE_NAME=${HELM_RELEASE_NAME:-$CI_ENVIRONMENT_SLUG}
- auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${RELEASE_NAME}-postgres:5432/${POSTGRES_DB}
- export DATABASE_URL=${DATABASE_URL-$auto_database_url}
- export TILLER_NAMESPACE=$KUBE_NAMESPACE
-
- function get_replicas() {
- track="${1:-stable}"
- percentage="${2:-100}"
-
- env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' )
- env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' )
-
- if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then
- # for stable track get number of replicas from `PRODUCTION_REPLICAS`
- eval new_replicas=\$${env_slug}_REPLICAS
- if [[ -z "$new_replicas" ]]; then
- new_replicas=$REPLICAS
- fi
- else
- # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
- eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
- if [[ -z "$new_replicas" ]]; then
- eval new_replicas=\${env_track}_REPLICAS
- fi
- fi
-
- replicas="${new_replicas:-1}"
- replicas="$(($replicas * $percentage / 100))"
-
- # always return at least one replicas
- if [[ $replicas -gt 0 ]]; then
- echo "$replicas"
- else
- echo 1
- fi
- }
-
- # Extracts variables prefixed with K8S_SECRET_
- # and creates a Kubernetes secret.
- #
- # e.g. If we have the following environment variables:
- # K8S_SECRET_A=value1
- # K8S_SECRET_B=multi\ word\ value
- #
- # Then we will create a secret with the following key-value pairs:
- # data:
- # A: dmFsdWUxCg==
- # B: bXVsdGkgd29yZCB2YWx1ZQo=
- function create_application_secret() {
- track="${1-stable}"
- export APPLICATION_SECRET_NAME=$(application_secret_name "$track")
-
- env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p" > k8s_prefixed_variables
-
- kubectl create secret \
- -n "$KUBE_NAMESPACE" generic "$APPLICATION_SECRET_NAME" \
- --from-env-file k8s_prefixed_variables -o yaml --dry-run |
- kubectl replace -n "$KUBE_NAMESPACE" --force -f -
-
- export APPLICATION_SECRET_CHECKSUM=$(cat k8s_prefixed_variables | sha256sum | cut -d ' ' -f 1)
-
- rm k8s_prefixed_variables
- }
-
- function deploy_name() {
- name="$RELEASE_NAME"
- track="${1-stable}"
-
- if [[ "$track" != "stable" ]]; then
- name="$name-$track"
- fi
-
- echo $name
- }
-
- function application_secret_name() {
- track="${1-stable}"
- name=$(deploy_name "$track")
-
- echo "${name}-secret"
- }
-
- function deploy() {
- track="${1-stable}"
- percentage="${2:-100}"
- name=$(deploy_name "$track")
-
- if [[ -z "$CI_COMMIT_TAG" ]]; then
- image_repository=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG}
- image_tag=${CI_APPLICATION_TAG:-$CI_COMMIT_SHA}
- else
- image_repository=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE}
- image_tag=${CI_APPLICATION_TAG:-$CI_COMMIT_TAG}
- fi
-
- service_enabled="true"
- postgres_enabled="$POSTGRES_ENABLED"
-
- # if track is different than stable,
- # re-use all attached resources
- if [[ "$track" != "stable" ]]; then
- service_enabled="false"
- postgres_enabled="false"
- fi
-
- replicas=$(get_replicas "$track" "$percentage")
-
- if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then
- secret_name='gitlab-registry'
- else
- secret_name=''
- fi
-
- create_application_secret "$track"
-
- env_slug=$(echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]')
- eval env_ADDITIONAL_HOSTS=\$${env_slug}_ADDITIONAL_HOSTS
- if [ -n "$env_ADDITIONAL_HOSTS" ]; then
- additional_hosts="{$env_ADDITIONAL_HOSTS}"
- elif [ -n "$ADDITIONAL_HOSTS" ]; then
- additional_hosts="{$ADDITIONAL_HOSTS}"
- fi
-
- if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
- echo "Deploying first release with database initialization..."
- helm upgrade --install \
- --wait \
- --set service.enabled="$service_enabled" \
- --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
- --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
- --set releaseOverride="$RELEASE_NAME" \
- --set image.repository="$image_repository" \
- --set image.tag="$image_tag" \
- --set image.pullPolicy=IfNotPresent \
- --set image.secrets[0].name="$secret_name" \
- --set application.track="$track" \
- --set application.database_url="$DATABASE_URL" \
- --set application.secretName="$APPLICATION_SECRET_NAME" \
- --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
- --set service.commonName="le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN" \
- --set service.url="$CI_ENVIRONMENT_URL" \
- --set service.additionalHosts="$additional_hosts" \
- --set replicaCount="$replicas" \
- --set postgresql.enabled="$postgres_enabled" \
- --set postgresql.nameOverride="postgres" \
- --set postgresql.postgresUser="$POSTGRES_USER" \
- --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
- --set postgresql.postgresDatabase="$POSTGRES_DB" \
- --set postgresql.imageTag="$POSTGRES_VERSION" \
- --set application.initializeCommand="$DB_INITIALIZE" \
- $HELM_UPGRADE_EXTRA_ARGS \
- --namespace="$KUBE_NAMESPACE" \
- "$name" \
- chart/
-
- echo "Deploying second release..."
- helm upgrade --reuse-values \
- --wait \
- --set application.initializeCommand="" \
- --set application.migrateCommand="$DB_MIGRATE" \
- $HELM_UPGRADE_EXTRA_ARGS \
- --namespace="$KUBE_NAMESPACE" \
- "$name" \
- chart/
- else
- echo "Deploying new release..."
- helm upgrade --install \
- --wait \
- --set service.enabled="$service_enabled" \
- --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
- --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
- --set releaseOverride="$RELEASE_NAME" \
- --set image.repository="$image_repository" \
- --set image.tag="$image_tag" \
- --set image.pullPolicy=IfNotPresent \
- --set image.secrets[0].name="$secret_name" \
- --set application.track="$track" \
- --set application.database_url="$DATABASE_URL" \
- --set application.secretName="$APPLICATION_SECRET_NAME" \
- --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
- --set service.commonName="le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN" \
- --set service.url="$CI_ENVIRONMENT_URL" \
- --set service.additionalHosts="$additional_hosts" \
- --set replicaCount="$replicas" \
- --set postgresql.enabled="$postgres_enabled" \
- --set postgresql.nameOverride="postgres" \
- --set postgresql.postgresUser="$POSTGRES_USER" \
- --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
- --set postgresql.postgresDatabase="$POSTGRES_DB" \
- --set postgresql.imageTag="$POSTGRES_VERSION" \
- --set application.migrateCommand="$DB_MIGRATE" \
- $HELM_UPGRADE_EXTRA_ARGS \
- --namespace="$KUBE_NAMESPACE" \
- "$name" \
- chart/
- fi
-
- if [[ -z "$ROLLOUT_STATUS_DISABLED" ]]; then
- kubectl rollout status -n "$KUBE_NAMESPACE" -w "$ROLLOUT_RESOURCE_TYPE/$name"
- fi
- }
-
- function scale() {
- track="${1-stable}"
- percentage="${2-100}"
- name=$(deploy_name "$track")
-
- replicas=$(get_replicas "$track" "$percentage")
-
- if [[ -n "$(helm ls -q "^$name$")" ]]; then
- helm upgrade --reuse-values \
- --wait \
- --set replicaCount="$replicas" \
- --namespace="$KUBE_NAMESPACE" \
- "$name" \
- chart/
- fi
- }
-
- function install_dependencies() {
- apk add -U openssl curl tar gzip bash ca-certificates git
- curl -sSL -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
- curl -sSL -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
- apk add glibc-2.28-r0.apk
- rm glibc-2.28-r0.apk
-
- curl -sS "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
- mv linux-amd64/helm /usr/bin/
- mv linux-amd64/tiller /usr/bin/
- helm version --client
- tiller -version
-
- curl -sSL -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
- chmod +x /usr/bin/kubectl
- kubectl version --client
- }
-
- function download_chart() {
- if [[ ! -d chart ]]; then
- auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app}
- auto_chart_name=$(basename $auto_chart)
- auto_chart_name=${auto_chart_name%.tgz}
- auto_chart_name=${auto_chart_name%.tar.gz}
- else
- auto_chart="chart"
- auto_chart_name="chart"
- fi
-
- helm init --client-only
- helm repo add ${AUTO_DEVOPS_CHART_REPOSITORY_NAME:-gitlab} ${AUTO_DEVOPS_CHART_REPOSITORY:-https://charts.gitlab.io} ${AUTO_DEVOPS_CHART_REPOSITORY_USERNAME:+"--username" "$AUTO_DEVOPS_CHART_REPOSITORY_USERNAME"} ${AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD:+"--password" "$AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD"}
- if [[ ! -d "$auto_chart" ]]; then
- helm fetch ${auto_chart} --untar
- fi
- if [ "$auto_chart_name" != "chart" ]; then
- mv ${auto_chart_name} chart
- fi
-
- helm dependency update chart/
- helm dependency build chart/
- }
-
- function ensure_namespace() {
- kubectl get namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
- }
-
- function check_kube_domain() {
- if [[ -z "$KUBE_INGRESS_BASE_DOMAIN" ]]; then
- echo "In order to deploy or use Review Apps,"
- echo "KUBE_INGRESS_BASE_DOMAIN variables must be set"
- echo "From 11.8, you can set KUBE_INGRESS_BASE_DOMAIN in cluster settings"
- echo "or by defining a variable at group or project level."
- echo "You can also manually add it in .gitlab-ci.yml"
- false
- else
- true
- fi
- }
-
- function initialize_tiller() {
- echo "Checking Tiller..."
-
- export HELM_HOST="localhost:44134"
- tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
- echo "Tiller is listening on ${HELM_HOST}"
-
- if ! helm version --debug; then
- echo "Failed to init Tiller."
- return 1
- fi
- echo ""
- }
-
- function create_secret() {
- echo "Create secret..."
- if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
- return
- fi
-
- kubectl create secret -n "$KUBE_NAMESPACE" \
- docker-registry gitlab-registry \
- --docker-server="$CI_REGISTRY" \
- --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
- --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
- --docker-email="$GITLAB_USER_EMAIL" \
- -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
- }
-
- function persist_environment_url() {
- echo $CI_ENVIRONMENT_URL > environment_url.txt
- }
-
- function delete() {
- track="${1-stable}"
- name=$(deploy_name "$track")
-
- if [[ -n "$(helm ls -q "^$name$")" ]]; then
- helm delete --purge "$name"
- fi
-
- secret_name=$(application_secret_name "$track")
- kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name"
- }
-
-before_script:
- - *deploy_helpers
diff --git a/lib/gitlab/database/subquery.rb b/lib/gitlab/database/subquery.rb
index 10971d2b274..2a6f39c6a27 100644
--- a/lib/gitlab/database/subquery.rb
+++ b/lib/gitlab/database/subquery.rb
@@ -6,11 +6,7 @@ module Gitlab
class << self
def self_join(relation)
t = relation.arel_table
- # Work around a bug in Rails 5, where LIMIT causes trouble
- # See https://gitlab.com/gitlab-org/gitlab-ce/issues/51729
- r = relation.limit(nil).arel
- r.take(relation.limit_value) if relation.limit_value
- t2 = r.as('t2')
+ t2 = relation.arel.as('t2')
relation.unscoped.joins(t.join(t2).on(t[:id].eq(t2[:id])).join_sources.first)
end
diff --git a/lib/gitlab/object_hierarchy.rb b/lib/gitlab/object_hierarchy.rb
index 38b32770e90..c06f106ffe1 100644
--- a/lib/gitlab/object_hierarchy.rb
+++ b/lib/gitlab/object_hierarchy.rb
@@ -32,11 +32,6 @@ module Gitlab
# Returns the maximum depth starting from the base
# A base object with no children has a maximum depth of `1`
def max_descendants_depth
- unless hierarchy_supported?
- # This makes the return value consistent with the case where hierarchy is supported
- return descendants_base.exists? ? 1 : nil
- end
-
base_and_descendants(with_depth: true).maximum(DEPTH_COLUMN)
end
@@ -66,8 +61,6 @@ module Gitlab
# each parent.
# rubocop: disable CodeReuse/ActiveRecord
def base_and_ancestors(upto: nil, hierarchy_order: nil)
- return ancestors_base unless hierarchy_supported?
-
recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all)
recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order
@@ -81,10 +74,6 @@ module Gitlab
# When `with_depth` is `true`, a `depth` column is included where it starts with `1` for the base objects
# and incremented as we go down the descendant tree
def base_and_descendants(with_depth: false)
- unless hierarchy_supported?
- return with_depth ? descendants_base.select("1 as #{DEPTH_COLUMN}", objects_table[Arel.star]) : descendants_base
- end
-
read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all))
end
@@ -112,8 +101,6 @@ module Gitlab
# If nested objects are not supported, ancestors_base is returned.
# rubocop: disable CodeReuse/ActiveRecord
def all_objects
- return ancestors_base unless hierarchy_supported?
-
ancestors = base_and_ancestors_cte
descendants = base_and_descendants_cte
@@ -135,10 +122,6 @@ module Gitlab
private
- def hierarchy_supported?
- Gitlab::Database.postgresql?
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def base_and_ancestors_cte(stop_id = nil, hierarchy_order = nil)
cte = SQL::RecursiveCTE.new(:base_and_ancestors)
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index 890228e5e78..615c0ec374c 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -166,7 +166,7 @@ module Gitlab
[model, times.count, times.sum]
end
- summarised_load_times.sort_by(&:last).reverse.each do |(model, query_count, time)|
+ summarised_load_times.sort_by(&:last).reverse_each do |(model, query_count, time)|
logger.info("#{model} total (#{query_count}): #{time.round(2)}ms")
end
end
diff --git a/lib/gitlab/project_authorizations.rb b/lib/gitlab/project_authorizations.rb
new file mode 100644
index 00000000000..a9270cd536e
--- /dev/null
+++ b/lib/gitlab/project_authorizations.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+# This class relies on Common Table Expressions to efficiently get all data,
+# including data for nested groups.
+module Gitlab
+ class ProjectAuthorizations
+ attr_reader :user
+
+ # user - The User object for which to calculate the authorizations.
+ def initialize(user)
+ @user = user
+ end
+
+ def calculate
+ cte = recursive_cte
+ cte_alias = cte.table.alias(Group.table_name)
+ projects = Project.arel_table
+ links = ProjectGroupLink.arel_table
+
+ relations = [
+ # The project a user has direct access to.
+ user.projects.select_for_project_authorization,
+
+ # The personal projects of the user.
+ user.personal_projects.select_as_maintainer_for_project_authorization,
+
+ # Projects that belong directly to any of the groups the user has
+ # access to.
+ Namespace
+ .unscoped
+ .select([alias_as_column(projects[:id], 'project_id'),
+ cte_alias[:access_level]])
+ .from(cte_alias)
+ .joins(:projects),
+
+ # Projects shared with any of the namespaces the user has access to.
+ Namespace
+ .unscoped
+ .select([
+ links[:project_id],
+ least(cte_alias[:access_level], links[:group_access], 'access_level')
+ ])
+ .from(cte_alias)
+ .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
+ .joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
+ .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
+ .where('p_ns.share_with_group_lock IS FALSE')
+ ]
+
+ ProjectAuthorization
+ .unscoped
+ .with
+ .recursive(cte.to_arel)
+ .select_from_union(relations)
+ end
+
+ private
+
+ # Builds a recursive CTE that gets all the groups the current user has
+ # access to, including any nested groups.
+ def recursive_cte
+ cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte)
+ members = Member.arel_table
+ namespaces = Namespace.arel_table
+
+ # Namespaces the user is a member of.
+ cte << user.groups
+ .select([namespaces[:id], members[:access_level]])
+ .except(:order)
+
+ # Sub groups of any groups the user is a member of.
+ cte << Group.select([
+ namespaces[:id],
+ greatest(members[:access_level], cte.table[:access_level], 'access_level')
+ ])
+ .joins(join_cte(cte))
+ .joins(join_members)
+ .except(:order)
+
+ cte
+ end
+
+ # Builds a LEFT JOIN to join optional memberships onto the CTE.
+ def join_members
+ members = Member.arel_table
+ namespaces = Namespace.arel_table
+
+ cond = members[:source_id]
+ .eq(namespaces[:id])
+ .and(members[:source_type].eq('Namespace'))
+ .and(members[:requested_at].eq(nil))
+ .and(members[:user_id].eq(user.id))
+
+ Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
+ end
+
+ # Builds an INNER JOIN to join namespaces onto the CTE.
+ def join_cte(cte)
+ namespaces = Namespace.arel_table
+ cond = cte.table[:id].eq(namespaces[:parent_id])
+
+ Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond))
+ end
+
+ def greatest(left, right, column_alias)
+ sql_function('GREATEST', [left, right], column_alias)
+ end
+
+ def least(left, right, column_alias)
+ sql_function('LEAST', [left, right], column_alias)
+ end
+
+ def sql_function(name, args, column_alias)
+ alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias)
+ end
+
+ def alias_as_column(value, alias_to)
+ Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to))
+ end
+ end
+end
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
deleted file mode 100644
index 2372a316ab0..00000000000
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module ProjectAuthorizations
- # Calculating new project authorizations when supporting nested groups.
- #
- # This class relies on Common Table Expressions to efficiently get all data,
- # including data for nested groups. As a result this class can only be used
- # on PostgreSQL.
- class WithNestedGroups
- attr_reader :user
-
- # user - The User object for which to calculate the authorizations.
- def initialize(user)
- @user = user
- end
-
- def calculate
- cte = recursive_cte
- cte_alias = cte.table.alias(Group.table_name)
- projects = Project.arel_table
- links = ProjectGroupLink.arel_table
-
- relations = [
- # The project a user has direct access to.
- user.projects.select_for_project_authorization,
-
- # The personal projects of the user.
- user.personal_projects.select_as_maintainer_for_project_authorization,
-
- # Projects that belong directly to any of the groups the user has
- # access to.
- Namespace
- .unscoped
- .select([alias_as_column(projects[:id], 'project_id'),
- cte_alias[:access_level]])
- .from(cte_alias)
- .joins(:projects),
-
- # Projects shared with any of the namespaces the user has access to.
- Namespace
- .unscoped
- .select([links[:project_id],
- least(cte_alias[:access_level],
- links[:group_access],
- 'access_level')])
- .from(cte_alias)
- .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
- .joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
- .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
- .where('p_ns.share_with_group_lock IS FALSE')
- ]
-
- ProjectAuthorization
- .unscoped
- .with
- .recursive(cte.to_arel)
- .select_from_union(relations)
- end
-
- private
-
- # Builds a recursive CTE that gets all the groups the current user has
- # access to, including any nested groups.
- def recursive_cte
- cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte)
- members = Member.arel_table
- namespaces = Namespace.arel_table
-
- # Namespaces the user is a member of.
- cte << user.groups
- .select([namespaces[:id], members[:access_level]])
- .except(:order)
-
- # Sub groups of any groups the user is a member of.
- cte << Group.select([namespaces[:id],
- greatest(members[:access_level],
- cte.table[:access_level], 'access_level')])
- .joins(join_cte(cte))
- .joins(join_members)
- .except(:order)
-
- cte
- end
-
- # Builds a LEFT JOIN to join optional memberships onto the CTE.
- def join_members
- members = Member.arel_table
- namespaces = Namespace.arel_table
-
- cond = members[:source_id]
- .eq(namespaces[:id])
- .and(members[:source_type].eq('Namespace'))
- .and(members[:requested_at].eq(nil))
- .and(members[:user_id].eq(user.id))
-
- Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
- end
-
- # Builds an INNER JOIN to join namespaces onto the CTE.
- def join_cte(cte)
- namespaces = Namespace.arel_table
- cond = cte.table[:id].eq(namespaces[:parent_id])
-
- Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond))
- end
-
- def greatest(left, right, column_alias)
- sql_function('GREATEST', [left, right], column_alias)
- end
-
- def least(left, right, column_alias)
- sql_function('LEAST', [left, right], column_alias)
- end
-
- def sql_function(name, args, column_alias)
- alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias)
- end
-
- def alias_as_column(value, alias_to)
- Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to))
- end
- end
- end
-end
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
deleted file mode 100644
index 50b41b17649..00000000000
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module ProjectAuthorizations
- # Calculating new project authorizations when not supporting nested groups.
- class WithoutNestedGroups
- attr_reader :user
-
- # user - The User object for which to calculate the authorizations.
- def initialize(user)
- @user = user
- end
-
- def calculate
- relations = [
- # Projects the user is a direct member of
- user.projects.select_for_project_authorization,
-
- # Personal projects
- user.personal_projects.select_as_maintainer_for_project_authorization,
-
- # Projects of groups the user is a member of
- user.groups_projects.select_for_project_authorization,
-
- # Projects shared with groups the user is a member of
- user.groups.joins(:shared_projects).select_for_project_authorization
- ]
-
- ProjectAuthorization
- .unscoped
- .select_from_union(relations)
- end
- end
- end
-end
diff --git a/lib/gitlab/push_options.rb b/lib/gitlab/push_options.rb
index b96590af08e..682edfc4259 100644
--- a/lib/gitlab/push_options.rb
+++ b/lib/gitlab/push_options.rb
@@ -6,9 +6,11 @@ module Gitlab
merge_request: {
keys: [
:create,
+ :description,
:merge_when_pipeline_succeeds,
:remove_source_branch,
- :target
+ :target,
+ :title
]
},
ci: {
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index e43147a3f37..21614ea003e 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -94,6 +94,12 @@ module Gitlab
}mx
end
+ # Based on Jira's project key format
+ # https://confluence.atlassian.com/adminjiraserver073/changing-the-project-key-format-861253229.html
+ def jira_issue_key_regex
+ @jira_issue_key_regex ||= /[A-Z][A-Z_0-9]+-\d+/
+ end
+
def jira_transition_id_regex
@jira_transition_id_regex ||= /\d+/
end
diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb
index 671d795ec33..4b10f921ed8 100644
--- a/lib/gitlab/sidekiq_middleware/memory_killer.rb
+++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb
@@ -14,9 +14,12 @@ module Gitlab
# shut Sidekiq down
MUTEX = Mutex.new
+ attr_reader :worker
+
def call(worker, job, queue)
yield
+ @worker = worker
current_rss = get_rss
return unless MAX_RSS > 0 && current_rss > MAX_RSS
@@ -25,9 +28,11 @@ module Gitlab
# Return if another thread is already waiting to shut Sidekiq down
next unless MUTEX.try_lock
- Sidekiq.logger.warn "Sidekiq worker PID-#{pid} current RSS #{current_rss}"\
- " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}"
- Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later"
+ warn("Sidekiq worker PID-#{pid} current RSS #{current_rss}"\
+ " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}")
+
+ warn("Sidekiq worker PID-#{pid} will stop fetching new jobs"\
+ " in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later")
# Wait `GRACE_TIME` to give the memory intensive job time to finish.
# Then, tell Sidekiq to stop fetching new jobs.
@@ -59,24 +64,28 @@ module Gitlab
def wait_and_signal_pgroup(time, signal, explanation)
return wait_and_signal(time, signal, explanation) unless Process.getpgrp == pid
- Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})"
+ warn("waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal)
sleep(time)
- Sidekiq.logger.warn "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})"
+ warn("sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal)
Process.kill(signal, 0)
end
def wait_and_signal(time, signal, explanation)
- Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
+ warn("waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal)
sleep(time)
- Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
+ warn("sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal)
Process.kill(signal, pid)
end
def pid
Process.pid
end
+
+ def warn(message, signal: nil)
+ Sidekiq.logger.warn(class: worker.class, pid: pid, signal: signal, message: message)
+ end
end
end
end
diff --git a/lib/gitlab/submodule_links.rb b/lib/gitlab/submodule_links.rb
index a6c0369d864..18fd604a3b0 100644
--- a/lib/gitlab/submodule_links.rb
+++ b/lib/gitlab/submodule_links.rb
@@ -9,7 +9,7 @@ module Gitlab
end
def for(submodule, sha)
- submodule_url = submodule_url_for(sha)[submodule.path]
+ submodule_url = submodule_url_for(sha, submodule.path)
SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository)
end
@@ -17,10 +17,15 @@ module Gitlab
attr_reader :repository
- def submodule_url_for(sha)
- strong_memoize(:"submodule_links_for_#{sha}") do
+ def submodule_urls_for(sha)
+ strong_memoize(:"submodule_urls_for_#{sha}") do
repository.submodule_urls_for(sha)
end
end
+
+ def submodule_url_for(sha, path)
+ urls = submodule_urls_for(sha)
+ urls && urls[path]
+ end
end
end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index 31c9e18c996..c66ce0434a4 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -22,7 +22,7 @@ module Gitlab
end
def force_utf8(str)
- str.force_encoding(Encoding::UTF_8)
+ str.dup.force_encoding(Encoding::UTF_8)
end
def ensure_utf8_size(str, bytes:)
diff --git a/lib/prometheus/pid_provider.rb b/lib/prometheus/pid_provider.rb
index c92522c73c5..e0f7e7e0a9e 100644
--- a/lib/prometheus/pid_provider.rb
+++ b/lib/prometheus/pid_provider.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'prometheus/client/support/unicorn'
-
module Prometheus
module PidProvider
extend self
@@ -10,29 +8,38 @@ module Prometheus
if Sidekiq.server?
'sidekiq'
elsif defined?(Unicorn::Worker)
- "unicorn_#{unicorn_worker_id}"
+ unicorn_worker_id
elsif defined?(::Puma)
- "puma_#{puma_worker_id}"
+ puma_worker_id
else
- "process_#{Process.pid}"
+ unknown_process_id
end
end
private
- # This is not fully accurate as we don't really know if the nil returned
- # is actually means we're on master or not.
- # Follow up issue was created to address this problem and
- # to introduce more structrured approach to a current process discovery:
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/64740
def unicorn_worker_id
- ::Prometheus::Client::Support::Unicorn.worker_id || 'master'
+ if matches = process_name.match(/unicorn.*worker\[([0-9]+)\]/)
+ "unicorn_#{matches[1]}"
+ elsif process_name =~ /unicorn/
+ "unicorn_master"
+ else
+ unknown_process_id
+ end
end
- # See the comment for #unicorn_worker_id
def puma_worker_id
- match = process_name.match(/cluster worker ([0-9]+):/)
- match ? match[1] : 'master'
+ if matches = process_name.match(/puma.*cluster worker ([0-9]+):/)
+ "puma_#{matches[1]}"
+ elsif process_name =~ /puma/
+ "puma_master"
+ else
+ unknown_process_id
+ end
+ end
+
+ def unknown_process_id
+ "process_#{Process.pid}"
end
def process_name
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 5f2ed5274e4..d054959e05e 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -33,7 +33,7 @@ namespace :gitlab do
# Removes the entry from the array
tables.delete 'schema_migrations'
# Truncate schema_migrations to ensure migrations re-run
- connection.execute('TRUNCATE schema_migrations') if connection.data_source_exists? 'schema_migrations'
+ connection.execute('TRUNCATE schema_migrations') if connection.table_exists? 'schema_migrations'
# Drop tables with cascade to avoid dependent table errors
# PG: http://www.postgresql.org/docs/current/static/ddl-depend.html
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bc644925281..58c75ee2319 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1239,6 +1239,9 @@ msgstr ""
msgid "Are you sure you want to delete this %{typeOfComment}?"
msgstr ""
+msgid "Are you sure you want to delete this board?"
+msgstr ""
+
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
@@ -1636,6 +1639,12 @@ msgstr ""
msgid "Blog"
msgstr ""
+msgid "Board name"
+msgstr ""
+
+msgid "Board scope"
+msgstr ""
+
msgid "BoardBlankState|Add default lists"
msgstr ""
@@ -2425,6 +2434,9 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
+msgid "ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path."
+msgstr ""
+
msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
@@ -2662,6 +2674,9 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
+msgid "ClusterIntegration|Project namespace prefix (optional, unique)"
+msgstr ""
+
msgid "ClusterIntegration|Prometheus"
msgstr ""
@@ -2770,6 +2785,9 @@ msgstr ""
msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
msgstr ""
+msgid "ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals."
+msgstr ""
+
msgid "ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid."
msgstr ""
@@ -3279,6 +3297,9 @@ msgstr ""
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
+msgid "Create board"
+msgstr ""
+
msgid "Create branch"
msgstr ""
@@ -3318,6 +3339,9 @@ msgstr ""
msgid "Create milestone"
msgstr ""
+msgid "Create new board"
+msgstr ""
+
msgid "Create new branch"
msgstr ""
@@ -3546,6 +3570,9 @@ msgstr ""
msgid "Delete Snippet"
msgstr ""
+msgid "Delete board"
+msgstr ""
+
msgid "Delete comment"
msgstr ""
@@ -3938,6 +3965,9 @@ msgstr ""
msgid "Edit application"
msgstr ""
+msgid "Edit board"
+msgstr ""
+
msgid "Edit comment"
msgstr ""
@@ -3962,6 +3992,12 @@ msgstr ""
msgid "Edit public deploy key"
msgstr ""
+msgid "Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}"
+msgstr ""
+
+msgid "Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported"
+msgstr ""
+
msgid "Email"
msgstr ""
@@ -4118,6 +4154,9 @@ msgstr ""
msgid "Enter at least three characters to search"
msgstr ""
+msgid "Enter board name"
+msgstr ""
+
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
@@ -4634,6 +4673,9 @@ msgstr ""
msgid "Failed to create resources"
msgstr ""
+msgid "Failed to delete board. Please try again."
+msgstr ""
+
msgid "Failed to deploy to"
msgstr ""
@@ -5433,6 +5475,9 @@ msgstr ""
msgid "IDE|Review"
msgstr ""
+msgid "IDE|Successful commit"
+msgstr ""
+
msgid "IP Address"
msgstr ""
@@ -6394,18 +6439,12 @@ msgstr ""
msgid "Mark to do as done"
msgstr ""
-msgid "Markdown"
-msgstr ""
-
msgid "Markdown Help"
msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "Markdown is supported"
-msgstr ""
-
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr ""
@@ -7460,6 +7499,12 @@ msgstr ""
msgid "PerformanceBar|Gitaly calls"
msgstr ""
+msgid "PerformanceBar|Redis calls"
+msgstr ""
+
+msgid "PerformanceBar|Rugged calls"
+msgstr ""
+
msgid "PerformanceBar|SQL queries"
msgstr ""
@@ -11639,6 +11684,9 @@ msgstr ""
msgid "Unable to resolve"
msgstr ""
+msgid "Unable to save your changes. Please try again."
+msgstr ""
+
msgid "Unable to schedule a pipeline to run immediately"
msgstr ""
@@ -13317,9 +13365,6 @@ msgstr ""
msgid "project avatar"
msgstr ""
-msgid "quick actions"
-msgstr ""
-
msgid "register"
msgstr ""
diff --git a/package.json b/package.json
index 4264064c93d..773918524f9 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.67.0",
- "@gitlab/ui": "^5.7.1",
+ "@gitlab/ui": "^5.9.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-link": "^1.2.11",
@@ -99,7 +99,7 @@
"mermaid": "^8.2.3",
"monaco-editor": "^0.15.6",
"monaco-editor-webpack-plugin": "^1.7.0",
- "mousetrap": "^1.4.6",
+ "mousetrap": "1.4.6",
"pdfjs-dist": "^2.0.943",
"pikaday": "^1.6.1",
"popper.js": "^1.14.7",
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index b59540d0377..507dccb52d0 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -70,7 +70,10 @@ module QA
end
def select_labels_and_refresh(labels)
- click_element(:edit_link_labels)
+ Support::Retrier.retry_until do
+ click_element(:edit_link_labels)
+ has_element?(:dropdown_menu_labels, text: labels.first)
+ end
labels.each do |label|
within_element(:dropdown_menu_labels, text: label) do
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 0918445d119..64aab9be056 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -59,7 +59,7 @@ module QA
end
def set_visibility(visibility)
- choose visibility
+ choose visibility.capitalize
end
def click_github_link
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
index b8c5c563da6..ae826fb3a32 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -8,33 +8,32 @@ module QA
include Common
view 'app/views/projects/settings/ci_cd/show.html.haml' do
- element :autodevops_settings
- element :runners_settings
- element :variables_settings
+ element :autodevops_settings_content
+ element :runners_settings_content
+ element :variables_settings_content
end
view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do
- element :enable_auto_devops_field, 'check_box :enabled' # rubocop:disable QA/ElementWithPattern
- element :enable_auto_devops_button, "%strong= s_('CICD|Default to Auto DevOps pipeline')" # rubocop:disable QA/ElementWithPattern
- element :save_changes_button, "submit _('Save changes')" # rubocop:disable QA/ElementWithPattern
+ element :enable_autodevops_checkbox
+ element :save_changes_button
end
def expand_runners_settings(&block)
- expand_section(:runners_settings) do
+ expand_section(:runners_settings_content) do
Settings::Runners.perform(&block)
end
end
def expand_ci_variables(&block)
- expand_section(:variables_settings) do
+ expand_section(:variables_settings_content) do
Settings::CiVariables.perform(&block)
end
end
def enable_auto_devops
- expand_section(:autodevops_settings) do
- check 'Default to Auto DevOps pipeline'
- click_on 'Save changes'
+ expand_section(:autodevops_settings_content) do
+ check_element :enable_autodevops_checkbox
+ click_element :save_changes_button
end
end
end
diff --git a/qa/qa/page/project/sub_menus/project.rb b/qa/qa/page/project/sub_menus/project.rb
index 5e0ee3c274a..6f1bc131f84 100644
--- a/qa/qa/page/project/sub_menus/project.rb
+++ b/qa/qa/page/project/sub_menus/project.rb
@@ -10,7 +10,7 @@ module QA
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
- element :link_project
+ element :project_link
end
end
end
@@ -18,7 +18,7 @@ module QA
def click_project
retry_on_exception do
within_sidebar do
- click_element(:link_project)
+ click_element(:project_link)
end
end
end
diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb
index c0a6004fe27..93a82094776 100644
--- a/qa/qa/resource/project.rb
+++ b/qa/qa/resource/project.rb
@@ -8,6 +8,7 @@ module QA
include Events::Project
attr_writer :initialize_with_readme
+ attr_writer :visibility
attribute :id
attribute :name
@@ -44,6 +45,7 @@ module QA
@standalone = false
@description = 'My awesome project'
@initialize_with_readme = false
+ @visibility = 'public'
end
def name=(raw_name)
@@ -60,7 +62,7 @@ module QA
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
- page.set_visibility('Public')
+ page.set_visibility(@visibility)
page.enable_initialize_with_readme if @initialize_with_readme
page.create_new_project
end
@@ -88,7 +90,7 @@ module QA
post_body = {
name: name,
description: description,
- visibility: 'public',
+ visibility: @visibility,
initialize_with_readme: @initialize_with_readme
}
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 21bfd2876a9..363980acc33 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -46,7 +46,7 @@ RSpec.configure do |config|
if ENV['CI']
config.around do |example|
- retry_times = example.metadata.keys.include?(:quarantine) ? 1 : 2
+ retry_times = example.metadata.key?(:quarantine) ? 1 : 2
example.run_with_retry retry: retry_times
end
end
diff --git a/scripts/insert-rspec-profiling-data b/scripts/insert-rspec-profiling-data
index b34379764e0..88c9d8c12b1 100755
--- a/scripts/insert-rspec-profiling-data
+++ b/scripts/insert-rspec-profiling-data
@@ -14,10 +14,6 @@ module RspecProfiling
Result.establish_connection(results_url)
end
- def prepared?
- connection.data_source_exists?(table)
- end
-
def results_url
ENV['RSPEC_PROFILING_POSTGRES_URL']
end
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index a1f6fb6bc0f..4f617b15a6e 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -69,7 +69,6 @@ function get_pod() {
break
fi
- printf "."
let "elapsed_seconds+=interval"
sleep ${interval}
done
@@ -360,7 +359,6 @@ function wait_for_review_app_to_be_accessible() {
break
fi
- printf "."
let "elapsed_seconds+=interval"
sleep ${interval}
done
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 0db58fbefc1..d54f7ad33cf 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -85,7 +85,7 @@ describe Boards::IssuesController do
expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1))
end
- it 'avoids N+1 database queries when adding a subgroup, project, and issue', :nested_groups do
+ it 'avoids N+1 database queries when adding a subgroup, project, and issue' do
create(:project, group: sub_group_1)
create(:labeled_issue, project: project, labels: [development])
control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb
index aa3cd690e3f..835c3d9b3af 100644
--- a/spec/controllers/concerns/group_tree_spec.rb
+++ b/spec/controllers/concerns/group_tree_spec.rb
@@ -30,7 +30,7 @@ describe GroupTree do
expect(assigns(:groups)).to contain_exactly(other_group)
end
- context 'for subgroups', :nested_groups do
+ context 'for subgroups' do
it 'only renders root groups when no parent was given' do
create(:group, :public, parent: group)
@@ -85,7 +85,7 @@ describe GroupTree do
expect(json_response.first['id']).to eq(group.id)
end
- context 'nested groups', :nested_groups do
+ context 'nested groups' do
it 'expands the tree when filtering' do
subgroup = create(:group, :public, parent: group, name: 'filter')
diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb
index 48373d29412..20a0951423b 100644
--- a/spec/controllers/dashboard/groups_controller_spec.rb
+++ b/spec/controllers/dashboard/groups_controller_spec.rb
@@ -26,7 +26,7 @@ describe Dashboard::GroupsController do
expect(assigns(:groups)).to contain_exactly(member_of_group)
end
- context 'when rendering an expanded hierarchy with public groups you are not a member of', :nested_groups do
+ context 'when rendering an expanded hierarchy with public groups you are not a member of' do
let!(:top_level_result) { create(:group, name: 'chef-top') }
let!(:top_level_a) { create(:group, name: 'top-a') }
let!(:sub_level_result_a) { create(:group, name: 'chef-sub-a', parent: top_level_a) }
diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb
index 02fb971bd9a..bced300a24c 100644
--- a/spec/controllers/groups/children_controller_spec.rb
+++ b/spec/controllers/groups/children_controller_spec.rb
@@ -46,7 +46,7 @@ describe Groups::ChildrenController do
end
end
- context 'for subgroups', :nested_groups do
+ context 'for subgroups' do
let!(:public_subgroup) { create(:group, :public, parent: group) }
let!(:private_subgroup) { create(:group, :private, parent: group) }
let!(:public_project) { create(:project, :public, namespace: group) }
@@ -292,7 +292,7 @@ describe Groups::ChildrenController do
end
end
- context 'with subgroups and projects', :nested_groups do
+ context 'with subgroups and projects' do
let!(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) }
let!(:other_subgroup) { create(:group, :public, parent: group) }
let!(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) }
diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb
index 3cc6fc6f066..98a4c50fc49 100644
--- a/spec/controllers/groups/labels_controller_spec.rb
+++ b/spec/controllers/groups/labels_controller_spec.rb
@@ -24,7 +24,7 @@ describe Groups::LabelsController do
expect(label_ids).to match_array([label_1.title, group_label_1.title])
end
- context 'with ancestor group', :nested_groups do
+ context 'with ancestor group' do
set(:subgroup) { create(:group, parent: group) }
set(:subgroup_label_1) { create(:group_label, group: subgroup, title: 'subgroup_label_1') }
@@ -32,7 +32,7 @@ describe Groups::LabelsController do
subgroup.add_owner(user)
end
- it 'returns ancestor group labels', :nested_groups do
+ it 'returns ancestor group labels' do
get :index, params: { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true }, format: :json
label_ids = json_response.map {|label| label['title']}
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index d2faef5b12b..404e61c5271 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -89,7 +89,7 @@ describe GroupsController do
end
describe 'GET #new' do
- context 'when creating subgroups', :nested_groups do
+ context 'when creating subgroups' do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
before do
@@ -166,7 +166,7 @@ describe GroupsController do
end
end
- context 'when creating subgroups', :nested_groups do
+ context 'when creating subgroups' do
[true, false].each do |can_create_group_status|
context "and can_create_group is #{can_create_group_status}" do
context 'and logged in as Owner' do
@@ -584,7 +584,7 @@ describe GroupsController do
end
end
- describe 'PUT transfer', :postgresql do
+ describe 'PUT transfer' do
before do
sign_in(user)
end
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 64a66502732..38388c21749 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -231,7 +231,7 @@ describe Import::BitbucketController do
end
end
- context 'user has chosen an existing nested namespace and name for the project', :postgresql do
+ context 'user has chosen an existing nested namespace and name for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
@@ -250,7 +250,7 @@ describe Import::BitbucketController do
end
end
- context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do
+ context 'user has chosen a non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
@@ -281,7 +281,7 @@ describe Import::BitbucketController do
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 5af7572e74e..e465eca6c71 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -197,7 +197,7 @@ describe Import::GitlabController do
end
end
- context 'user has chosen an existing nested namespace for the project', :postgresql do
+ context 'user has chosen an existing nested namespace for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
@@ -215,7 +215,7 @@ describe Import::GitlabController do
end
end
- context 'user has chosen a non-existent nested namespaces for the project', :postgresql do
+ context 'user has chosen a non-existent nested namespaces for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
@@ -246,7 +246,7 @@ describe Import::GitlabController do
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index f11880122b1..fa71d9b61b1 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -885,10 +885,9 @@ describe Projects::MergeRequestsController do
environment2 = create(:environment, project: forked)
create(:deployment, :succeed, environment: environment2, sha: sha, ref: 'master', deployable: build)
- # TODO address the last 11 queries
+ # TODO address the last 5 queries
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/63952 (5 queries)
- # And https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 (6 queries)
- leeway = 11
+ leeway = 5
expect { get_ci_environments_status }.not_to exceed_all_query_limit(control_count + leeway)
end
end
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 767cee7d54a..9b2025b836c 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -115,7 +115,7 @@ describe Projects::MilestonesController do
end
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let!(:subgroup) { create(:group, :public, parent: group) }
let!(:subgroup_milestone) { create(:milestone, group: subgroup) }
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 8fca9e680dd..fcab4d73dca 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -77,6 +77,53 @@ describe Projects::RepositoriesController do
expect(response).to have_gitlab_http_status(404)
end
end
+
+ describe 'caching' do
+ it 'sets appropriate caching headers' do
+ get_archive
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['ETag']).to be_present
+ expect(response.header['Cache-Control']).to include('max-age=60, private')
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'sets appropriate caching headers' do
+ get_archive
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['ETag']).to be_present
+ expect(response.header['Cache-Control']).to include('max-age=60, public')
+ end
+ end
+
+ context 'when ref is a commit SHA' do
+ it 'max-age is set to 3600 in Cache-Control header' do
+ get_archive('ddd0f15ae83993f5cb66a927a28673882e99100b')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Cache-Control']).to include('max-age=3600')
+ end
+ end
+
+ context 'when If-None-Modified header is set' do
+ it 'returns a 304 status' do
+ # Get the archive cached first
+ get_archive
+
+ request.headers['If-None-Match'] = response.headers['ETag']
+ get_archive
+
+ expect(response).to have_gitlab_http_status(304)
+ end
+ end
+
+ def get_archive(id = 'feature')
+ get :archive, params: { namespace_id: project.namespace, project_id: project, id: id }, format: 'zip'
+ end
+ end
end
end
end
diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb
index 021971ac421..578af9ed895 100644
--- a/spec/factories/abuse_reports.rb
+++ b/spec/factories/abuse_reports.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :abuse_report do
reporter factory: :user
diff --git a/spec/factories/appearances.rb b/spec/factories/appearances.rb
index dd5129229d4..bdd5964fb93 100644
--- a/spec/factories/appearances.rb
+++ b/spec/factories/appearances.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Read about factories at https://github.com/thoughtbot/factory_bot
FactoryBot.define do
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
index 00c063c49f8..90b6b9e648a 100644
--- a/spec/factories/application_settings.rb
+++ b/spec/factories/application_settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :application_setting do
default_projects_limit 42
diff --git a/spec/factories/award_emoji.rb b/spec/factories/award_emoji.rb
index 43753fa650c..a8bb806381e 100644
--- a/spec/factories/award_emoji.rb
+++ b/spec/factories/award_emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :award_emoji do
name "thumbsup"
diff --git a/spec/factories/badge.rb b/spec/factories/badge.rb
index b87ece946cb..1d4e29014cc 100644
--- a/spec/factories/badge.rb
+++ b/spec/factories/badge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
trait :base_badge do
link_url { generate(:url) }
diff --git a/spec/factories/boards.rb b/spec/factories/boards.rb
index 6524bb4830c..a5aff5c7504 100644
--- a/spec/factories/boards.rb
+++ b/spec/factories/boards.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :board do
transient do
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index 1a2be5e9552..2a30e2034b1 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :broadcast_message do
message "MyText"
diff --git a/spec/factories/chat_names.rb b/spec/factories/chat_names.rb
index 56993e5da18..07bf990162f 100644
--- a/spec/factories/chat_names.rb
+++ b/spec/factories/chat_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :chat_name, class: ChatName do
user factory: :user
diff --git a/spec/factories/chat_teams.rb b/spec/factories/chat_teams.rb
index d048c46d6b6..52628e6d53d 100644
--- a/spec/factories/chat_teams.rb
+++ b/spec/factories/chat_teams.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :chat_team, class: ChatTeam do
sequence(:team_id) { |n| "abcdefghijklm#{n}" }
diff --git a/spec/factories/ci/bridge.rb b/spec/factories/ci/bridge.rb
index 7cb5900f2b7..6491b9dca19 100644
--- a/spec/factories/ci/bridge.rb
+++ b/spec/factories/ci/bridge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_bridge, class: Ci::Bridge do
name 'bridge'
diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb
index 3e8e2736423..492dc47f083 100644
--- a/spec/factories/ci/build_trace_chunks.rb
+++ b/spec/factories/ci/build_trace_chunks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_build_trace_chunk, class: Ci::BuildTraceChunk do
build factory: :ci_build
diff --git a/spec/factories/ci/build_trace_section_names.rb b/spec/factories/ci/build_trace_section_names.rb
index ce07e716dde..e52694ef3dc 100644
--- a/spec/factories/ci/build_trace_section_names.rb
+++ b/spec/factories/ci/build_trace_section_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_build_trace_section_name, class: Ci::BuildTraceSectionName do
sequence(:name) { |n| "section_#{n}" }
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 5f7c75a3a92..e3b7c64176a 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
include ActionDispatch::TestProcess
FactoryBot.define do
diff --git a/spec/factories/ci/group_variables.rb b/spec/factories/ci/group_variables.rb
index 9bf520a2c0a..13c2b78e61b 100644
--- a/spec/factories/ci/group_variables.rb
+++ b/spec/factories/ci/group_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_group_variable, class: Ci::GroupVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 542fa9775cd..2d68a8e9fe3 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
include ActionDispatch::TestProcess
FactoryBot.define do
diff --git a/spec/factories/ci/pipeline_schedule.rb b/spec/factories/ci/pipeline_schedule.rb
index 4b83ba2ac1b..8fae6986869 100644
--- a/spec/factories/ci/pipeline_schedule.rb
+++ b/spec/factories/ci/pipeline_schedule.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_pipeline_schedule, class: Ci::PipelineSchedule do
cron '0 1 * * *'
diff --git a/spec/factories/ci/pipeline_schedule_variables.rb b/spec/factories/ci/pipeline_schedule_variables.rb
index c85b97fbfc7..fd7cfada65b 100644
--- a/spec/factories/ci/pipeline_schedule_variables.rb
+++ b/spec/factories/ci/pipeline_schedule_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_pipeline_schedule_variable, class: Ci::PipelineScheduleVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
diff --git a/spec/factories/ci/pipeline_variables.rb b/spec/factories/ci/pipeline_variables.rb
index b18055d7b3a..af0982124d7 100644
--- a/spec/factories/ci/pipeline_variables.rb
+++ b/spec/factories/ci/pipeline_variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_pipeline_variable, class: Ci::PipelineVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index aa5ccbda6cd..9652b0000a9 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_empty_pipeline, class: Ci::Pipeline do
source :push
diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb
index ec15972c423..bc28544a839 100644
--- a/spec/factories/ci/runner_projects.rb
+++ b/spec/factories/ci/runner_projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_runner_project, class: Ci::RunnerProject do
runner factory: [:ci_runner, :project]
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 24e70913b87..1e4344b814d 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_runner, class: Ci::Runner do
sequence(:description) { |n| "My runner#{n}" }
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index ce61e6bf759..88ff8d7dc53 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_stage, class: Ci::LegacyStage do
skip_create
diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb
index 0e9fc3d0014..d63bf9868c9 100644
--- a/spec/factories/ci/trigger_requests.rb
+++ b/spec/factories/ci/trigger_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_trigger_request, class: Ci::TriggerRequest do
trigger factory: :ci_trigger
diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb
index 742d9efba2d..6f628ed5435 100644
--- a/spec/factories/ci/triggers.rb
+++ b/spec/factories/ci/triggers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_trigger_without_token, class: Ci::Trigger do
owner
diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb
index 97a7c9ba252..55d11085040 100644
--- a/spec/factories/ci/variables.rb
+++ b/spec/factories/ci/variables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :ci_variable, class: Ci::Variable do
sequence(:key) { |n| "VARIABLE_#{n}" }
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index d78f01828d7..7fe9ea6801a 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :clusters_applications_helm, class: Clusters::Applications::Helm do
cluster factory: %i(cluster provided_by_gcp)
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index 171f5256d2b..b0d14b672f4 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :cluster, class: Clusters::Cluster do
user
diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb
index bf30a9c3a61..d5dc288fddb 100644
--- a/spec/factories/clusters/platforms/kubernetes.rb
+++ b/spec/factories/clusters/platforms/kubernetes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :cluster_platform_kubernetes, class: Clusters::Platforms::Kubernetes do
cluster
diff --git a/spec/factories/clusters/providers/gcp.rb b/spec/factories/clusters/providers/gcp.rb
index 186c7c8027c..22462651b6a 100644
--- a/spec/factories/clusters/providers/gcp.rb
+++ b/spec/factories/clusters/providers/gcp.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :cluster_provider_gcp, class: Clusters::Providers::Gcp do
cluster
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 848a31e96c1..a76da30217e 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb
index 2bcc4b6cf52..d1554426a76 100644
--- a/spec/factories/commits.rb
+++ b/spec/factories/commits.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../support/helpers/repo_helpers'
FactoryBot.define do
diff --git a/spec/factories/container_repositories.rb b/spec/factories/container_repositories.rb
index 00fad7975c9..a9771200d6e 100644
--- a/spec/factories/container_repositories.rb
+++ b/spec/factories/container_repositories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :container_repository do
name 'test_image'
diff --git a/spec/factories/conversational_development_index_metrics.rb b/spec/factories/conversational_development_index_metrics.rb
index abf37fb861e..ea5816684c6 100644
--- a/spec/factories/conversational_development_index_metrics.rb
+++ b/spec/factories/conversational_development_index_metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :conversational_development_index_metric, class: ConversationalDevelopmentIndex::Metric do
leader_issues 9.256
diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb
index 4350652fb79..7f82902dee7 100644
--- a/spec/factories/deploy_keys_projects.rb
+++ b/spec/factories/deploy_keys_projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :deploy_keys_project do
deploy_key
diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb
index 017e866e69c..a96258f5cbe 100644
--- a/spec/factories/deploy_tokens.rb
+++ b/spec/factories/deploy_tokens.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :deploy_token do
token { SecureRandom.hex(50) }
diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb
index 1c7787bc1a6..89ff1c527df 100644
--- a/spec/factories/deployments.rb
+++ b/spec/factories/deployments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :deployment, class: Deployment do
sha 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb
index feacd5ccf15..284ba631c37 100644
--- a/spec/factories/emails.rb
+++ b/spec/factories/emails.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :email do
user
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index 9d9e3d693b8..b5c8f0ca4f0 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :environment, class: Environment do
sequence(:name) { |n| "environment#{n}" }
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index bf8411b1894..b15eb1592fc 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :event do
project
diff --git a/spec/factories/file_uploaders.rb b/spec/factories/file_uploaders.rb
index 8404985bfea..ec8f5c9af2d 100644
--- a/spec/factories/file_uploaders.rb
+++ b/spec/factories/file_uploaders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :file_uploader do
skip_create
diff --git a/spec/factories/fork_network_members.rb b/spec/factories/fork_network_members.rb
index 850e3f158f1..d34de72ab60 100644
--- a/spec/factories/fork_network_members.rb
+++ b/spec/factories/fork_network_members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :fork_network_member do
association :project
diff --git a/spec/factories/fork_networks.rb b/spec/factories/fork_networks.rb
index 813b1943eb2..12ea688bbce 100644
--- a/spec/factories/fork_networks.rb
+++ b/spec/factories/fork_networks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :fork_network do
association :root_project, factory: :project
diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb
index 5034c3d0e48..954b5338846 100644
--- a/spec/factories/gitaly/commit.rb
+++ b/spec/factories/gitaly/commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
sequence(:gitaly_commit_id) { Digest::SHA1.hexdigest(Time.now.to_f.to_s) }
diff --git a/spec/factories/gitaly/commit_author.rb b/spec/factories/gitaly/commit_author.rb
index aaf634ce08b..51dcd8a623b 100644
--- a/spec/factories/gitaly/commit_author.rb
+++ b/spec/factories/gitaly/commit_author.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :gitaly_commit_author, class: Gitaly::CommitAuthor do
skip_create
diff --git a/spec/factories/gitaly/tag.rb b/spec/factories/gitaly/tag.rb
index e99776d524a..a7a84753090 100644
--- a/spec/factories/gitaly/tag.rb
+++ b/spec/factories/gitaly/tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :gitaly_tag, class: Gitaly::Tag do
skip_create
diff --git a/spec/factories/gpg_key_subkeys.rb b/spec/factories/gpg_key_subkeys.rb
index 6c7db5379a9..9daf26d930d 100644
--- a/spec/factories/gpg_key_subkeys.rb
+++ b/spec/factories/gpg_key_subkeys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :gpg_key_subkey do
gpg_key
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index 3c0f43cc1b6..9f321643174 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../support/helpers/gpg_helpers'
FactoryBot.define do
diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb
index b89e6ffc4b3..a0fc1740d77 100644
--- a/spec/factories/gpg_signature.rb
+++ b/spec/factories/gpg_signature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :gpg_signature do
commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
diff --git a/spec/factories/group_custom_attributes.rb b/spec/factories/group_custom_attributes.rb
index d2f45d5d3ce..895bc42c84b 100644
--- a/spec/factories/group_custom_attributes.rb
+++ b/spec/factories/group_custom_attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :group_custom_attribute do
group
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 077c6ddc5ae..3bf9cdef253 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :group_member do
access_level { GroupMember::OWNER }
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index b67ab955ffc..334c0f369cd 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :group, class: Group, parent: :namespace do
sequence(:name) { |n| "group#{n}" }
diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb
index 122d0d42938..21cfe7fe623 100644
--- a/spec/factories/identities.rb
+++ b/spec/factories/identities.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :identity do
provider 'ldapmain'
diff --git a/spec/factories/import_export_uploads.rb b/spec/factories/import_export_uploads.rb
index 7750d49b1d0..8521411e0e8 100644
--- a/spec/factories/import_export_uploads.rb
+++ b/spec/factories/import_export_uploads.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :import_export_upload do
project { create(:project) }
diff --git a/spec/factories/import_states.rb b/spec/factories/import_states.rb
index d6de26dccbc..8e778200389 100644
--- a/spec/factories/import_states.rb
+++ b/spec/factories/import_states.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :import_state, class: ProjectImportState do
status :none
diff --git a/spec/factories/instance_configuration.rb b/spec/factories/instance_configuration.rb
index 31866a9c221..daa0be3f404 100644
--- a/spec/factories/instance_configuration.rb
+++ b/spec/factories/instance_configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :instance_configuration do
skip_create
diff --git a/spec/factories/internal_ids.rb b/spec/factories/internal_ids.rb
index fbde07a391a..df5c5beeb42 100644
--- a/spec/factories/internal_ids.rb
+++ b/spec/factories/internal_ids.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :internal_id do
project
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index ab27fee33dc..434225f7022 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :issue do
title { generate(:title) }
diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb
index 3f0c60f32b7..087d2521836 100644
--- a/spec/factories/keys.rb
+++ b/spec/factories/keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../support/helpers/key_generator_helper'
FactoryBot.define do
diff --git a/spec/factories/label_links.rb b/spec/factories/label_links.rb
index 007847d9cf4..9b7c0a158bd 100644
--- a/spec/factories/label_links.rb
+++ b/spec/factories/label_links.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :label_link do
label
diff --git a/spec/factories/label_priorities.rb b/spec/factories/label_priorities.rb
index c4824faad53..2f9f2ddfa89 100644
--- a/spec/factories/label_priorities.rb
+++ b/spec/factories/label_priorities.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :label_priority do
project
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index f759b6d499d..3eed750be03 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
trait :base_label do
title { generate(:label_title) }
diff --git a/spec/factories/lfs_file_locks.rb b/spec/factories/lfs_file_locks.rb
index b9d24f82b65..73675d076ab 100644
--- a/spec/factories/lfs_file_locks.rb
+++ b/spec/factories/lfs_file_locks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :lfs_file_lock do
user
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index 08ac7fb69e7..631d87cfb12 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
include ActionDispatch::TestProcess
FactoryBot.define do
diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb
index 4804d0bb884..7b55cc57f75 100644
--- a/spec/factories/lfs_objects_projects.rb
+++ b/spec/factories/lfs_objects_projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :lfs_objects_project do
lfs_object
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index 210c58b21e9..e68611ec518 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :list do
board
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 0b6a43b13a9..3d12ff98257 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :merge_request do
title { generate(:title) }
diff --git a/spec/factories/merge_requests_closing_issues.rb b/spec/factories/merge_requests_closing_issues.rb
index ee0606a72b6..94f8b27358c 100644
--- a/spec/factories/merge_requests_closing_issues.rb
+++ b/spec/factories/merge_requests_closing_issues.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :merge_requests_closing_issues do
issue
diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb
index 027349b2f8e..7d623000fc9 100644
--- a/spec/factories/milestones.rb
+++ b/spec/factories/milestones.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :milestone do
title
diff --git a/spec/factories/namespaces.rb b/spec/factories/namespaces.rb
index 0cfc6e3aa46..09dbe16ef9e 100644
--- a/spec/factories/namespaces.rb
+++ b/spec/factories/namespaces.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :namespace do
sequence(:name) { |n| "namespace#{n}" }
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index c51f2f958f9..5b9a7e6f864 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../support/helpers/repo_helpers'
include ActionDispatch::TestProcess
diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb
index 5116ef33f5d..c16b0e456ba 100644
--- a/spec/factories/notification_settings.rb
+++ b/spec/factories/notification_settings.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :notification_setting do
source factory: :project
diff --git a/spec/factories/oauth_access_grants.rb b/spec/factories/oauth_access_grants.rb
index 02c51cd9899..7a46ae6d71d 100644
--- a/spec/factories/oauth_access_grants.rb
+++ b/spec/factories/oauth_access_grants.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :oauth_access_grant do
resource_owner_id { create(:user).id }
diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb
index eabfd6cd830..8d1075dacbb 100644
--- a/spec/factories/oauth_access_tokens.rb
+++ b/spec/factories/oauth_access_tokens.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :oauth_access_token do
resource_owner
diff --git a/spec/factories/oauth_applications.rb b/spec/factories/oauth_applications.rb
index 4427da1d6c7..4748b320298 100644
--- a/spec/factories/oauth_applications.rb
+++ b/spec/factories/oauth_applications.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :oauth_application, class: 'Doorkeeper::Application', aliases: [:application] do
sequence(:name) { |n| "OAuth App #{n}" }
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index e441dfcf229..ee5be82cd19 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :pages_domain, class: 'PagesDomain' do
sequence(:domain) { |n| "my#{n}.domain.com" }
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index e7fd22a96b2..cc9b2328705 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :personal_access_token do
user
diff --git a/spec/factories/pool_repositories.rb b/spec/factories/pool_repositories.rb
index 8cac666069c..7a8946d47b0 100644
--- a/spec/factories/pool_repositories.rb
+++ b/spec/factories/pool_repositories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :pool_repository do
shard { Shard.by_name("default") }
diff --git a/spec/factories/programming_languages.rb b/spec/factories/programming_languages.rb
index d3511d42b6c..ee8e7765ec9 100644
--- a/spec/factories/programming_languages.rb
+++ b/spec/factories/programming_languages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :programming_language do
name 'Ruby'
diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb
index 1de42512402..4cc59c7095c 100644
--- a/spec/factories/project_auto_devops.rb
+++ b/spec/factories/project_auto_devops.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_auto_devops do
project
diff --git a/spec/factories/project_custom_attributes.rb b/spec/factories/project_custom_attributes.rb
index 099d2d7ff19..c9ed86cc0ac 100644
--- a/spec/factories/project_custom_attributes.rb
+++ b/spec/factories/project_custom_attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_custom_attribute do
project
diff --git a/spec/factories/project_deploy_tokens.rb b/spec/factories/project_deploy_tokens.rb
index 4866cb58d88..fbf5454b0fe 100644
--- a/spec/factories/project_deploy_tokens.rb
+++ b/spec/factories/project_deploy_tokens.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_deploy_token do
project
diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb
index 59c77627ee5..b02d167a950 100644
--- a/spec/factories/project_group_links.rb
+++ b/spec/factories/project_group_links.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_group_link do
project
diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb
index a448d565e4b..96c9742c7d0 100644
--- a/spec/factories/project_hooks.rb
+++ b/spec/factories/project_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_hook do
url { generate(:url) }
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index c72e0487895..6dcac0400ca 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_member do
user
diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb
index 3d4174eb852..f084d9d5cbf 100644
--- a/spec/factories/project_statistics.rb
+++ b/spec/factories/project_statistics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_statistics do
project
diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb
index 4d21ed47f39..401402614f4 100644
--- a/spec/factories/project_wikis.rb
+++ b/spec/factories/project_wikis.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :project_wiki do
skip_create
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 0e8810b73a1..afe27aaf1fb 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative '../support/helpers/test_env'
FactoryBot.define do
diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb
index 5457c0d2a8f..741615bc0d3 100644
--- a/spec/factories/protected_branches.rb
+++ b/spec/factories/protected_branches.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :protected_branch do
name
diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb
index 2b81d089549..6ff2a245b58 100644
--- a/spec/factories/protected_tags.rb
+++ b/spec/factories/protected_tags.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :protected_tag do
name
diff --git a/spec/factories/redirect_routes.rb b/spec/factories/redirect_routes.rb
index 774232d0b34..8fa0aec007c 100644
--- a/spec/factories/redirect_routes.rb
+++ b/spec/factories/redirect_routes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :redirect_route do
sequence(:path) { |n| "redirect#{n}" }
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 4cacc77c182..34794f57284 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :release do
tag "v1.1.0"
diff --git a/spec/factories/remote_mirrors.rb b/spec/factories/remote_mirrors.rb
index adc7da27522..ff1d751c86c 100644
--- a/spec/factories/remote_mirrors.rb
+++ b/spec/factories/remote_mirrors.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :remote_mirror, class: 'RemoteMirror' do
association :project, :repository
diff --git a/spec/factories/repository_languages.rb b/spec/factories/repository_languages.rb
index 1757ba6766c..b2b17ebbce2 100644
--- a/spec/factories/repository_languages.rb
+++ b/spec/factories/repository_languages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :repository_language do
project
diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb
index b0174dd06b7..11116af7dbb 100644
--- a/spec/factories/sent_notifications.rb
+++ b/spec/factories/sent_notifications.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :sent_notification do
project
diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb
index f2b6e7a11f9..b6f2d6d8389 100644
--- a/spec/factories/sequences.rb
+++ b/spec/factories/sequences.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
sequence(:username) { |n| "user#{n}" }
sequence(:name) { |n| "John Doe#{n}" }
diff --git a/spec/factories/service_hooks.rb b/spec/factories/service_hooks.rb
index c907862b4f6..ff819f4f8d0 100644
--- a/spec/factories/service_hooks.rb
+++ b/spec/factories/service_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :service_hook do
url { generate(:url) }
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index daf842e3075..5ef39b3e818 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :service do
project
diff --git a/spec/factories/shards.rb b/spec/factories/shards.rb
index c095fa5f0a0..c30a38180e8 100644
--- a/spec/factories/shards.rb
+++ b/spec/factories/shards.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :shard do
name "default"
diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb
index dc12b562108..9c3a0fbe9b3 100644
--- a/spec/factories/snippets.rb
+++ b/spec/factories/snippets.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :snippet do
author
diff --git a/spec/factories/spam_logs.rb b/spec/factories/spam_logs.rb
index a467f850a80..42a856832e7 100644
--- a/spec/factories/spam_logs.rb
+++ b/spec/factories/spam_logs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :spam_log do
user
diff --git a/spec/factories/subscriptions.rb b/spec/factories/subscriptions.rb
index 8f7ab74ec70..2b652cd57bf 100644
--- a/spec/factories/subscriptions.rb
+++ b/spec/factories/subscriptions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :subscription do
project
diff --git a/spec/factories/system_hooks.rb b/spec/factories/system_hooks.rb
index 9e00eeb6ef1..a2d163a060f 100644
--- a/spec/factories/system_hooks.rb
+++ b/spec/factories/system_hooks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :system_hook do
url { generate(:url) }
diff --git a/spec/factories/system_note_metadata.rb b/spec/factories/system_note_metadata.rb
index e913068da40..8cd4b77799c 100644
--- a/spec/factories/system_note_metadata.rb
+++ b/spec/factories/system_note_metadata.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :system_note_metadata do
note
diff --git a/spec/factories/term_agreements.rb b/spec/factories/term_agreements.rb
index 3c4eebd0196..b7e259bd44b 100644
--- a/spec/factories/term_agreements.rb
+++ b/spec/factories/term_agreements.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :term_agreement do
term
diff --git a/spec/factories/terms.rb b/spec/factories/terms.rb
index 5ffca365a5f..b890261d293 100644
--- a/spec/factories/terms.rb
+++ b/spec/factories/terms.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :term, class: ApplicationSetting::Term do
terms "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
diff --git a/spec/factories/timelogs.rb b/spec/factories/timelogs.rb
index b45f06b9a0a..056a8833c46 100644
--- a/spec/factories/timelogs.rb
+++ b/spec/factories/timelogs.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Read about factories at https://github.com/thoughtbot/factory_bot
FactoryBot.define do
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index ed3d87eb76b..2ff024112a4 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :todo do
project
diff --git a/spec/factories/trending_project.rb b/spec/factories/trending_project.rb
index f7c634fd21f..354b8c1e002 100644
--- a/spec/factories/trending_project.rb
+++ b/spec/factories/trending_project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
# TrendingProject
factory :trending_project, class: 'TrendingProject' do
diff --git a/spec/factories/u2f_registrations.rb b/spec/factories/u2f_registrations.rb
index 26090b08966..c968468834b 100644
--- a/spec/factories/u2f_registrations.rb
+++ b/spec/factories/u2f_registrations.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :u2f_registration do
certificate { FFaker::BaconIpsum.characters(728) }
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
index 426abdc2a6c..2ede92c8af0 100644
--- a/spec/factories/uploads.rb
+++ b/spec/factories/uploads.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :upload do
model { build(:project) }
diff --git a/spec/factories/user_agent_details.rb b/spec/factories/user_agent_details.rb
index 7183a8e1140..055aea50585 100644
--- a/spec/factories/user_agent_details.rb
+++ b/spec/factories/user_agent_details.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :user_agent_detail do
ip_address '127.0.0.1'
diff --git a/spec/factories/user_callouts.rb b/spec/factories/user_callouts.rb
index 528e442c14b..c4a217fd357 100644
--- a/spec/factories/user_callouts.rb
+++ b/spec/factories/user_callouts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :user_callout do
feature_name :gke_cluster_integration
diff --git a/spec/factories/user_custom_attributes.rb b/spec/factories/user_custom_attributes.rb
index a184a2e0f17..7bd5c06f4ef 100644
--- a/spec/factories/user_custom_attributes.rb
+++ b/spec/factories/user_custom_attributes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :user_custom_attribute do
user
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 4f3392cdcbf..b2c8bdab013 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :user, aliases: [:author, :assignee, :recipient, :owner, :resource_owner] do
email { generate(:email) }
diff --git a/spec/factories/users_star_projects.rb b/spec/factories/users_star_projects.rb
index 6afd08a2084..a1299024fe4 100644
--- a/spec/factories/users_star_projects.rb
+++ b/spec/factories/users_star_projects.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :users_star_project do
project
diff --git a/spec/factories/web_hook_log.rb b/spec/factories/web_hook_log.rb
index 17837260a4b..5750af85662 100644
--- a/spec/factories/web_hook_log.rb
+++ b/spec/factories/web_hook_log.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :web_hook_log do
web_hook factory: :project_hook
diff --git a/spec/factories/wiki_directories.rb b/spec/factories/wiki_directories.rb
index b105c82b19d..de23cf110b5 100644
--- a/spec/factories/wiki_directories.rb
+++ b/spec/factories/wiki_directories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
FactoryBot.define do
factory :wiki_directory do
skip_create
diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb
index ae257d769e8..761ba58edb2 100644
--- a/spec/factories/wiki_pages.rb
+++ b/spec/factories/wiki_pages.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'ostruct'
FactoryBot.define do
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index fb76e2b0014..bdab3c63244 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -27,7 +27,7 @@ describe 'Dashboard Groups page', :js do
expect(page).not_to have_content(another_group.name)
end
- it 'shows subgroups the user is member of', :nested_groups do
+ it 'shows subgroups the user is member of' do
group.add_owner(user)
nested_group.add_owner(user)
@@ -40,7 +40,7 @@ describe 'Dashboard Groups page', :js do
expect(page).to have_content(nested_group.name)
end
- context 'when filtering groups', :nested_groups do
+ context 'when filtering groups' do
before do
group.add_owner(user)
nested_group.add_owner(user)
@@ -79,7 +79,7 @@ describe 'Dashboard Groups page', :js do
end
end
- context 'with subgroups', :nested_groups do
+ context 'with subgroups' do
let!(:subgroup) { create(:group, :public, parent: group) }
before do
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index e4eb0d355d1..3fbc0f78d38 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -110,7 +110,7 @@ describe 'Group empty states' do
end
context 'group without a project' do
- context 'group has a subgroup', :nested_groups do
+ context 'group has a subgroup' do
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, namespace: subgroup) }
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 676769c25fe..257aeae7e23 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -121,7 +121,7 @@ describe 'Edit group settings' do
expect(find(:css, '.group-root-path').text).to eq(root_url)
end
- it 'has a parent group URL label for a subgroup group', :postgresql do
+ it 'has a parent group URL label for a subgroup group' do
subgroup = create(:group, parent: group)
visit edit_group_path(subgroup)
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index 0ada530781c..a16004c79ca 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -50,7 +50,7 @@ describe 'Group issues page' do
end
end
- context 'issues list', :nested_groups do
+ context 'issues list' do
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, :public, group: subgroup)}
let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb
index 4ba7161601e..ffff755793f 100644
--- a/spec/features/groups/members/list_members_spec.rb
+++ b/spec/features/groups/members/list_members_spec.rb
@@ -13,7 +13,7 @@ describe 'Groups > Members > List members' do
sign_in(user1)
end
- it 'show members from current group and parent', :nested_groups do
+ it 'show members from current group and parent' do
group.add_developer(user1)
nested_group.add_developer(user2)
@@ -23,7 +23,7 @@ describe 'Groups > Members > List members' do
expect(second_row.text).to include(user2.name)
end
- it 'show user once if member of both current group and parent', :nested_groups do
+ it 'show user once if member of both current group and parent' do
group.add_developer(user1)
nested_group.add_developer(user1)
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index 704d9f12888..86b523fbdd9 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -9,7 +9,7 @@ describe 'Group share with group lock' do
sign_in(root_owner)
end
- context 'with a subgroup', :nested_groups do
+ context 'with a subgroup' do
let!(:subgroup) { create(:group, parent: root_group) }
context 'when enabling the parent group share with group lock' do
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index bed998a0859..0d78bd23973 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -75,11 +75,7 @@ describe 'Group show page' do
sign_in(owner)
end
- context 'when subgroups are supported', :nested_groups do
- before do
- allow(Group).to receive(:supports_nested_objects?) { true }
- end
-
+ context 'when subgroups are supported' do
it 'allows creating subgroups' do
visit path
@@ -87,19 +83,6 @@ describe 'Group show page' do
.to have_css("li[data-text='New subgroup']", visible: false)
end
end
-
- context 'when subgroups are not supported' do
- before do
- allow(Group).to receive(:supports_nested_objects?) { false }
- end
-
- it 'does not allow creating subgroups' do
- visit path
-
- expect(page)
- .not_to have_selector("li[data-text='New subgroup']", visible: false)
- end
- end
end
context 'for maintainers' do
@@ -107,11 +90,7 @@ describe 'Group show page' do
sign_in(maintainer)
end
- context 'when subgroups are supported', :nested_groups do
- before do
- allow(Group).to receive(:supports_nested_objects?) { true }
- end
-
+ context 'when subgroups are supported' do
context 'when subgroup_creation_level is set to maintainers' do
before do
relaxed_group.add_maintainer(maintainer)
@@ -141,19 +120,6 @@ describe 'Group show page' do
end
end
end
-
- context 'when subgroups are not supported' do
- before do
- allow(Group).to receive(:supports_nested_objects?) { false }
- end
-
- it 'does not allow creating subgroups' do
- visit path
-
- expect(page)
- .not_to have_selector("li[data-text='New subgroup']", visible: false)
- end
- end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 8e7f78cab81..ad645643919 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -105,7 +105,7 @@ describe 'Group' do
end
end
- describe 'create a nested group', :nested_groups, :js do
+ describe 'create a nested group', :js do
let(:group) { create(:group, path: 'foo') }
context 'as admin' do
@@ -231,7 +231,7 @@ describe 'Group' do
end
end
- describe 'group page with nested groups', :nested_groups, :js do
+ describe 'group page with nested groups', :js do
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
let!(:project) { create(:project, namespace: group) }
diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb
index 6eb59ef72c2..65989c36c1e 100644
--- a/spec/features/ide_spec.rb
+++ b/spec/features/ide_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe 'IDE', :js do
- describe 'sub-groups', :nested_groups do
+ describe 'sub-groups' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
index a90cdd8d920..90b499efd72 100644
--- a/spec/features/import/manifest_import_spec.rb
+++ b/spec/features/import/manifest_import_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do
+describe 'Import multiple repositories by uploading a manifest file', :js do
include Select2Helper
let(:user) { create(:admin) }
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 489651fea15..45bd2218ee6 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Labels Hierarchy', :js, :nested_groups do
+describe 'Labels Hierarchy', :js do
include FilteredSearchHelpers
let!(:user) { create(:user) }
diff --git a/spec/features/markdown/metrics_spec.rb b/spec/features/markdown/metrics_spec.rb
new file mode 100644
index 00000000000..aa53ac50c78
--- /dev/null
+++ b/spec/features/markdown/metrics_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching do
+ include PrometheusHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:prometheus_project) }
+ let(:environment) { create(:environment, project: project) }
+ let(:issue) { create(:issue, project: project, description: description) }
+ let(:description) { "See [metrics dashboard](#{metrics_url}) for info." }
+ let(:metrics_url) { metrics_project_environment_url(project, environment) }
+
+ before do
+ configure_host
+ import_common_metrics
+ stub_any_prometheus_request_with_response
+
+ project.add_developer(user)
+
+ sign_in(user)
+ end
+
+ after do
+ restore_host
+ end
+
+ context 'with deployments and related deployable present' do
+ it 'shows embedded metrics' do
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('div.prometheus-graph')
+ expect(page).to have_text('Memory Usage (Total)')
+ expect(page).to have_text('Core Usage (Total)')
+ end
+ end
+
+ def import_common_metrics
+ ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
+ end
+
+ def configure_host
+ @original_default_host = default_url_options[:host]
+ @original_gitlab_url = Gitlab.config.gitlab[:url]
+
+ # Ensure we create a metrics url with the right host.
+ # Configure host for route helpers in specs (also updates root_url):
+ default_url_options[:host] = Capybara.server_host
+
+ # Ensure we identify urls with the appropriate host.
+ # Configure host to include port in app:
+ Gitlab.config.gitlab[:url] = root_url.chomp('/')
+ end
+
+ def restore_host
+ default_url_options[:host] = @original_default_host
+ Gitlab.config.gitlab[:url] = @original_gitlab_url
+ end
+end
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index 86331728f88..54705cd51b0 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -34,6 +34,7 @@ describe 'OAuth Login', :js, :allow_forgery_protection do
before do
stub_omniauth_config(provider)
+ expect(ActiveSession).to receive(:cleanup).with(user).at_least(:once).and_call_original
end
context 'when two-factor authentication is disabled' do
diff --git a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
index 5cb015e80be..69296ef00fd 100644
--- a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
+++ b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb
@@ -1,8 +1,8 @@
require "spec_helper"
describe "User downloads artifacts" do
- set(:project) { create(:project, :public) }
- set(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) }
+ set(:project) { create(:project, :repository, :public) }
+ set(:pipeline) { create(:ci_empty_pipeline, status: :success, sha: project.commit.id, project: project) }
set(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) }
shared_examples "downloading" do
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index fc74a370e72..818939c1b96 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -13,7 +13,7 @@ describe 'User browses commits' do
it 'renders commit' do
visit project_commit_path(project, sample_commit.id)
- expect(page).to have_content(sample_commit.message.gsub!(/\s+/, ' '))
+ expect(page).to have_content(sample_commit.message.gsub(/\s+/, ' '))
.and have_content("Showing #{sample_commit.files_changed_count} changed files")
.and have_content('Side-by-side')
end
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index 7432c600c1e..c5423ec4662 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -58,7 +58,7 @@ describe 'Project > Members > Invite group', :js do
end
end
- context 'for a project in a subgroup', :nested_groups do
+ context 'for a project in a subgroup' do
let!(:group_to_share_with) { create(:group) }
let(:root_group) { create(:group) }
let(:subgroup) { create(:group, parent: root_group) }
@@ -181,7 +181,7 @@ describe 'Project > Members > Invite group', :js do
group_to_share_with.add_maintainer(maintainer)
end
- it 'the groups dropdown does not show ancestors', :nested_groups do
+ it 'the groups dropdown does not show ancestors' do
visit project_settings_members_path(project)
click_on 'invite-group-tab'
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 7602935b47e..8cdaed43fbc 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -294,7 +294,7 @@ describe 'New project' do
end
end
- context 'from manifest file', :postgresql do
+ context 'from manifest file' do
before do
first('.import_manifest').click
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index bf0c0de89b2..31fdd308292 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -126,7 +126,7 @@ describe "Projects > Settings > Pipelines settings" do
end
end
- context 'when auto devops is turned on group parent level', :nested_groups do
+ context 'when auto devops is turned on group parent level' do
before do
group = create(:group, parent: create(:group, :auto_devops_enabled))
project.update!(namespace: group)
diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb
index 2fdbc04fa62..9af2e47a334 100644
--- a/spec/features/projects/settings/user_transfers_a_project_spec.rb
+++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb
@@ -68,7 +68,7 @@ describe 'Projects > Settings > User transfers a project', :js do
end
end
- context 'when nested groups are available', :nested_groups do
+ context 'when nested groups are available' do
it 'allows transferring a project to a subgroup' do
subgroup = create(:group, parent: group)
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index 50e7e934cf6..8afbcf72ca7 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Subgroup Issuables', :js, :nested_groups do
+describe 'Subgroup Issuables', :js do
let!(:group) { create(:group, name: 'group') }
let!(:subgroup) { create(:group, parent: group, name: 'subgroup') }
let!(:project) { create(:project, namespace: subgroup, name: 'project') }
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
index c0932539131..19262a635ac 100644
--- a/spec/features/projects/user_creates_project_spec.rb
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -26,7 +26,7 @@ describe 'User creates a project', :js do
expect(page).to have_content(project.url_to_repo)
end
- context 'in a subgroup they do not own', :nested_groups do
+ context 'in a subgroup they do not own' do
let(:parent) { create(:group) }
let!(:subgroup) { create(:group, parent: parent) }
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index aac095bfa6b..80741ace5d6 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -132,9 +132,15 @@ describe "User creates wiki page" do
fill_in(:wiki_content, with: ascii_content)
- page.within(".wiki-form") do
- click_button("Create page")
- end
+ # This is the dumbest bug in the world:
+ # When the #wiki_content textarea is filled in, JS captures the `Enter` keydown event in order to do
+ # auto-indentation and manually inserts a newline. However, for whatever reason, when you try to click on the
+ # submit button in Capybara, it will not trigger the `click` event if a \n or \r character has been manually
+ # added to the textarea. It will, however, trigger ALL OTHER EVENTS, including `mouseover`/down/up, focus, and
+ # blur. Just not `click`. But only when you manually insert \n or \r - if you manually insert any other sequence
+ # then `click` is fired normally. And it's only Capybara. Browsers and JSDOM don't have this issue.
+ # So that's why the next line performs the click via JS.
+ page.execute_script("document.querySelector('.qa-create-page-button').click()")
page.within ".md" do
expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index b5112758475..d1e2d17e9cc 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -314,7 +314,7 @@ describe 'Project' do
end
end
- context 'for subgroups', :js, :nested_groups do
+ context 'for subgroups', :js do
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:project) { create(:project, :repository, group: subgroup) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index f380bc122a7..65dbae1c674 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -126,7 +126,7 @@ describe "Private Project Access" do
describe "GET /:project_path/blob" do
let(:commit) { project.repository.commit }
- subject { project_blob_path(project, File.join(commit.id, '.gitignore'))}
+ subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index efba303033b..1ea88010c89 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -132,7 +132,6 @@ describe 'Login' do
it 'does not show a "You are already signed in." error message' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
enter_code(user.current_otp)
@@ -144,7 +143,6 @@ describe 'Login' do
it 'allows login with valid code' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
enter_code(user.current_otp)
@@ -170,7 +168,6 @@ describe 'Login' do
it 'allows login with invalid code, then valid code' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
enter_code('foo')
@@ -179,6 +176,15 @@ describe 'Login' do
enter_code(user.current_otp)
expect(current_path).to eq root_path
end
+
+ it 'triggers ActiveSession.cleanup for the user' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+ expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original
+
+ enter_code(user.current_otp)
+ end
end
context 'using backup code' do
@@ -195,7 +201,6 @@ describe 'Login' do
it 'allows login' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
enter_code(codes.sample)
@@ -206,7 +211,6 @@ describe 'Login' do
it 'invalidates the used code' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
expect { enter_code(codes.sample) }
@@ -216,7 +220,6 @@ describe 'Login' do
it 'invalidates backup codes twice in a row' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter).twice
- .and increment(:user_session_override_counter).twice
.and increment(:user_two_factor_authenticated_counter).twice
.and increment(:user_session_destroyed_counter)
@@ -230,6 +233,15 @@ describe 'Login' do
expect { enter_code(codes.sample) }
.to change { user.reload.otp_backup_codes.size }.by(-1)
end
+
+ it 'triggers ActiveSession.cleanup for the user' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_two_factor_authenticated_counter)
+ expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original
+
+ enter_code(codes.sample)
+ end
end
context 'with invalid code' do
@@ -274,7 +286,7 @@ describe 'Login' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
+ expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original
sign_in_using_saml!
@@ -287,8 +299,8 @@ describe 'Login' do
it 'shows 2FA prompt after OAuth login' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
+ expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original
sign_in_using_saml!
@@ -329,6 +341,14 @@ describe 'Login' do
expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
+
+ it 'triggers ActiveSession.cleanup for the user' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original
+
+ gitlab_sign_in(user)
+ end
end
context 'with invalid username and password' do
@@ -649,7 +669,6 @@ describe 'Login' do
it 'asks the user to accept the terms' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
.and increment(:user_two_factor_authenticated_counter)
visit new_user_session_path
@@ -708,7 +727,6 @@ describe 'Login' do
it 'asks the user to accept the terms before setting an email' do
expect(authentication_metrics)
.to increment(:user_authenticated_counter)
- .and increment(:user_session_override_counter)
gitlab_sign_in_via('saml', user, 'my-uid')
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 6a9b281fb4c..5768f42c888 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -53,6 +53,19 @@ describe 'Users > User browses projects on user page', :js do
expect(page).to have_content(project2.name)
end
+ it 'does not have incorrectly interpolated message', :js do
+ project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since)
+
+ sign_in(user)
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ wait_for_requests
+
+ expect(page).to have_content(project.name)
+ expect(page).not_to have_content("_('Updated')")
+ end
+
context 'when not signed in' do
it 'renders user public project' do
visit user_path(user)
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
index bcde115b1a6..f3b54ca0461 100644
--- a/spec/finders/autocomplete/users_finder_spec.rb
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -50,7 +50,7 @@ describe Autocomplete::UsersFinder do
it { is_expected.to match_array([user1]) }
end
- context 'when passed a subgroup', :nested_groups do
+ context 'when passed a subgroup' do
let(:grandparent) { create(:group, :public) }
let(:parent) { create(:group, :public, parent: grandparent) }
let(:child) { create(:group, :public, parent: parent) }
diff --git a/spec/finders/cluster_ancestors_finder_spec.rb b/spec/finders/cluster_ancestors_finder_spec.rb
index 750042b6b54..4aedb41d446 100644
--- a/spec/finders/cluster_ancestors_finder_spec.rb
+++ b/spec/finders/cluster_ancestors_finder_spec.rb
@@ -32,7 +32,7 @@ describe ClusterAncestorsFinder, '#execute' do
is_expected.to eq([project_cluster, group_cluster, instance_cluster])
end
- context 'nested groups', :nested_groups do
+ context 'nested groups' do
let(:group) { create(:group, parent: parent_group) }
let(:parent_group) { create(:group) }
@@ -65,7 +65,7 @@ describe ClusterAncestorsFinder, '#execute' do
is_expected.to eq([group_cluster, instance_cluster])
end
- context 'nested groups', :nested_groups do
+ context 'nested groups' do
let(:group) { create(:group, parent: parent_group) }
let(:parent_group) { create(:group) }
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index c28fd7cad11..5fb6739d6e2 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -19,7 +19,7 @@ describe GroupDescendantsFinder do
expect(finder.has_children?).to be_truthy
end
- context 'when there are subgroups', :nested_groups do
+ context 'when there are subgroups' do
it 'is true when there are projects' do
create(:group, parent: group)
@@ -99,7 +99,7 @@ describe GroupDescendantsFinder do
)
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') }
let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') }
@@ -126,7 +126,7 @@ describe GroupDescendantsFinder do
end
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let!(:project) { create(:project, namespace: group) }
let!(:subgroup) { create(:group, :private, parent: group) }
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 8975ea0f063..49b0e14241e 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -18,7 +18,7 @@ describe GroupMembersFinder, '#execute' do
expect(result.to_a).to match_array([member3, member2, member1])
end
- it 'returns members for nested group', :nested_groups do
+ it 'returns members for nested group' do
group.add_developer(user2)
nested_group.request_access(user4)
member1 = group.add_maintainer(user1)
@@ -30,7 +30,7 @@ describe GroupMembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member3, member4])
end
- it 'returns members for descendant groups if requested', :nested_groups do
+ it 'returns members for descendant groups if requested' do
member1 = group.add_maintainer(user2)
member2 = group.add_maintainer(user1)
nested_group.add_maintainer(user2)
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index f8fcc2d0e40..f4bd8a3f6ba 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -19,7 +19,7 @@ describe GroupProjectsFinder do
context "only owned" do
let(:options) { { only_owned: true } }
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
@@ -33,7 +33,7 @@ describe GroupProjectsFinder do
end
context "all" do
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
@@ -78,7 +78,7 @@ describe GroupProjectsFinder do
subgroup_private_project.add_maintainer(current_user)
end
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
@@ -96,7 +96,7 @@ describe GroupProjectsFinder do
current_user.update(external: true)
end
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
@@ -111,7 +111,7 @@ describe GroupProjectsFinder do
end
context "all" do
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
@@ -153,7 +153,7 @@ describe GroupProjectsFinder do
context "only owned" do
let(:options) { { only_owned: true } }
- context 'with subgroups projects', :nested_groups do
+ context 'with subgroups projects' do
before do
options[:include_subgroups] = true
end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index 367ca43bdfe..c8875d1f92d 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -65,7 +65,7 @@ describe GroupsFinder do
end
end
- context 'subgroups', :nested_groups do
+ context 'subgroups' do
let(:user) { create(:user) }
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index bf38d083ca6..7fbe27c1939 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -51,7 +51,7 @@ describe IssuesFinder do
end
end
- context 'when include_subgroup param is true', :nested_groups do
+ context 'when include_subgroup param is true' do
before do
params[:include_subgroups] = true
end
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 98b4933fef6..ba41ded112a 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -89,7 +89,7 @@ describe LabelsFinder do
end
end
- context 'when including labels from group ancestors', :nested_groups do
+ context 'when including labels from group ancestors' do
it 'returns labels from group and its ancestors' do
private_group_1.add_developer(user)
private_subgroup_1.add_developer(user)
@@ -108,7 +108,7 @@ describe LabelsFinder do
end
end
- context 'when including labels from group descendants', :nested_groups do
+ context 'when including labels from group descendants' do
it 'returns labels from group and its descendants' do
private_group_1.add_developer(user)
private_subgroup_1.add_developer(user)
@@ -128,7 +128,7 @@ describe LabelsFinder do
end
end
- context 'filtering by project_id', :nested_groups do
+ context 'filtering by project_id' do
context 'when include_ancestor_groups is true' do
let!(:sub_project) { create(:project, namespace: private_subgroup_1 ) }
let!(:project_label) { create(:label, project: sub_project, title: 'Label 5') }
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 83348457caa..4203f58fe81 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -9,7 +9,7 @@ describe MembersFinder, '#execute' do
set(:user3) { create(:user) }
set(:user4) { create(:user) }
- it 'returns members for project and parent groups', :nested_groups do
+ it 'returns members for project and parent groups' do
nested_group.request_access(user1)
member1 = group.add_maintainer(user2)
member2 = nested_group.add_maintainer(user3)
@@ -20,7 +20,7 @@ describe MembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member2, member3])
end
- it 'includes nested group members if asked', :nested_groups do
+ it 'includes nested group members if asked' do
project = create(:project, namespace: group)
nested_group.request_access(user1)
member1 = group.add_maintainer(user2)
@@ -32,7 +32,7 @@ describe MembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member2, member3])
end
- context 'when include_invited_groups_members == true', :nested_groups do
+ context 'when include_invited_groups_members == true' do
subject { described_class.new(project, user2).execute(include_invited_groups_members: true) }
set(:linked_group) { create(:group, :public, :access_requestable) }
@@ -40,7 +40,7 @@ describe MembersFinder, '#execute' do
set(:linked_group_member) { linked_group.add_developer(user1) }
set(:nested_linked_group_member) { nested_linked_group.add_developer(user2) }
- it 'includes all the invited_groups members including members inherited from ancestor groups', :nested_groups do
+ it 'includes all the invited_groups members including members inherited from ancestor groups' do
create(:project_group_link, project: project, group: nested_linked_group)
expect(subject).to contain_exactly(linked_group_member, nested_linked_group_member)
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index da5e9dab058..78224f0b9da 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -42,7 +42,7 @@ describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
end
- it 'filters by group including subgroups', :nested_groups do
+ it 'filters by group including subgroups' do
params = { group_id: group.id, include_subgroups: true }
merge_requests = described_class.new(user, params).execute
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index 22318a9946a..f7b35e76925 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -36,7 +36,7 @@ describe TodosFinder do
expect(todos).to match_array([todo1])
end
- context 'with subgroups', :nested_groups do
+ context 'with subgroups' do
let(:subgroup) { create(:group, parent: group) }
let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) }
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
index 1ee1205e29a..5d779a323c2 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
@@ -1,6 +1,10 @@
{
"type": "object",
- "required": ["dashboard", "priority", "panel_groups"],
+ "required": [
+ "dashboard",
+ "priority",
+ "panel_groups"
+ ],
"properties": {
"dashboard": { "type": "string" },
"priority": { "type": "number" },
diff --git a/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json
index 1e62d020026..8fb66f6652b 100644
--- a/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json
+++ b/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json
@@ -7,7 +7,7 @@
"message": "Vulnerabilities in libxml2 in nokogiri",
"description": " The version of libxml2 packaged with Nokogiri contains several vulnerabilities.\r\n Nokogiri has mitigated these issues by upgrading to libxml 2.9.5.\r\n\r\n It was discovered that a type confusion error existed in libxml2. An\r\n attacker could use this to specially construct XML data that\r\n could cause a denial of service or possibly execute arbitrary\r\n code. (CVE-2017-0663)\r\n\r\n It was discovered that libxml2 did not properly validate parsed entity\r\n references. An attacker could use this to specially construct XML\r\n data that could expose sensitive information. (CVE-2017-7375)\r\n\r\n It was discovered that a buffer overflow existed in libxml2 when\r\n handling HTTP redirects. An attacker could use this to specially\r\n construct XML data that could cause a denial of service or possibly\r\n execute arbitrary code. (CVE-2017-7376)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered a buffer overflow in\r\n libxml2 when handling elements. An attacker could use this to specially\r\n construct XML data that could cause a denial of service or possibly\r\n execute arbitrary code. (CVE-2017-9047)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered a buffer overread\r\n in libxml2 when handling elements. An attacker could use this\r\n to specially construct XML data that could cause a denial of\r\n service. (CVE-2017-9048)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered multiple buffer overreads\r\n in libxml2 when handling parameter-entity references. An attacker\r\n could use these to specially construct XML data that could cause a\r\n denial of service. (CVE-2017-9049, CVE-2017-9050)",
"cve": "rails/Gemfile.lock:nokogiri:gemnasium:06565b64-486d-4326-b906-890d9915804d",
- "severity": "Unknown",
+ "severity": "High",
"solution": "Upgrade to latest version.",
"scanner": {
"id": "gemnasium",
@@ -48,7 +48,7 @@
"message": "Infinite recursion in parameter entities in nokogiri",
"description": "libxml2 incorrectly handles certain parameter entities. An attacker can leverage this with specially constructed XML data to cause libxml2 to consume resources, leading to a denial of service.",
"cve": "rails/Gemfile.lock:nokogiri:gemnasium:6a0d56f6-2441-492a-9b14-edb95ac31919",
- "severity": "Unknown",
+ "severity": "High",
"solution": "Upgrade to latest version.",
"scanner": {
"id": "gemnasium",
diff --git a/spec/frontend/helpers/indent_helper_spec.js b/spec/frontend/helpers/indent_helper_spec.js
new file mode 100644
index 00000000000..fca12f0d1ef
--- /dev/null
+++ b/spec/frontend/helpers/indent_helper_spec.js
@@ -0,0 +1,371 @@
+import IndentHelper from '~/helpers/indent_helper';
+
+function createMockTextarea() {
+ const el = document.createElement('textarea');
+ el.setCursor = pos => el.setSelectionRange(pos, pos);
+ el.setCursorToEnd = () => el.setCursor(el.value.length);
+ el.selection = () => [el.selectionStart, el.selectionEnd];
+ el.cursor = () => {
+ const [start, end] = el.selection();
+ return start === end ? start : undefined;
+ };
+ return el;
+}
+
+describe('indent_helper', () => {
+ let element;
+ let ih;
+
+ beforeEach(() => {
+ element = createMockTextarea();
+ ih = new IndentHelper(element);
+ });
+
+ describe('indents', () => {
+ describe('a single line', () => {
+ it('when on an empty line; and cursor follows', () => {
+ element.value = '';
+ ih.indent();
+ expect(element.value).toBe(' ');
+ expect(element.cursor()).toBe(4);
+ ih.indent();
+ expect(element.value).toBe(' ');
+ expect(element.cursor()).toBe(8);
+ });
+
+ it('when at the start of a line; and cursor stays at start', () => {
+ element.value = 'foobar';
+ element.setCursor(0);
+ ih.indent();
+ expect(element.value).toBe(' foobar');
+ expect(element.cursor()).toBe(4);
+ });
+
+ it('when the cursor is in the middle; and cursor follows', () => {
+ element.value = 'foobar';
+ element.setCursor(3);
+ ih.indent();
+ expect(element.value).toBe(' foobar');
+ expect(element.cursor()).toBe(7);
+ });
+ });
+
+ describe('several lines', () => {
+ it('when everything is selected; and everything remains selected', () => {
+ element.value = 'foo\nbar\nbaz';
+ element.setSelectionRange(0, 11);
+ ih.indent();
+ expect(element.value).toBe(' foo\n bar\n baz');
+ expect(element.selection()).toEqual([0, 23]);
+ });
+
+ it('when all lines are partially selected; and the selection adapts', () => {
+ element.value = 'foo\nbar\nbaz';
+ element.setSelectionRange(2, 9);
+ ih.indent();
+ expect(element.value).toBe(' foo\n bar\n baz');
+ expect(element.selection()).toEqual([6, 21]);
+ });
+
+ it('when some lines are entirely selected; and entire lines remain selected', () => {
+ element.value = 'foo\nbar\nbaz';
+ element.setSelectionRange(4, 11);
+ ih.indent();
+ expect(element.value).toBe('foo\n bar\n baz');
+ expect(element.selection()).toEqual([4, 19]);
+ });
+
+ it('when some lines are partially selected; and the selection adapts', () => {
+ element.value = 'foo\nbar\nbaz';
+ element.setSelectionRange(5, 9);
+ ih.indent();
+ expect(element.value).toBe('foo\n bar\n baz');
+ expect(element.selection()).toEqual([5 + 4, 9 + 2 * 4]);
+ });
+
+ it('having different indentation when some lines are entirely selected; and entire lines remain selected', () => {
+ element.value = ' foo\nbar\n baz';
+ element.setSelectionRange(8, 19);
+ ih.indent();
+ expect(element.value).toBe(' foo\n bar\n baz');
+ expect(element.selection()).toEqual([8, 27]);
+ });
+
+ it('having different indentation when some lines are partially selected; and the selection adapts', () => {
+ element.value = ' foo\nbar\n baz';
+ element.setSelectionRange(9, 14);
+ ih.indent();
+ expect(element.value).toBe(' foo\n bar\n baz');
+ expect(element.selection()).toEqual([13, 22]);
+ });
+ });
+ });
+
+ describe('unindents', () => {
+ describe('a single line', () => {
+ it('but does nothing if there is not indent', () => {
+ element.value = 'foobar';
+ element.setCursor(2);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(2);
+ });
+
+ it('but does nothing if there is a partial indent', () => {
+ element.value = ' foobar';
+ element.setCursor(1);
+ ih.unindent();
+ expect(element.value).toBe(' foobar');
+ expect(element.cursor()).toBe(1);
+ });
+
+ it('when the cursor is in the line text; cursor follows', () => {
+ element.value = ' foobar';
+ element.setCursor(6);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(2);
+ });
+
+ it('when the cursor is in the indent; and cursor goes to start', () => {
+ element.value = ' foobar';
+ element.setCursor(2);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(0);
+ });
+
+ it('when the cursor is at line start; and cursor stays at start', () => {
+ element.value = ' foobar';
+ element.setCursor(0);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(0);
+ });
+
+ it('when a selection includes part of the indent and text', () => {
+ element.value = ' foobar';
+ element.setSelectionRange(2, 8);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.selection()).toEqual([0, 4]);
+ });
+
+ it('when a selection includes part of the indent only', () => {
+ element.value = ' foobar';
+ element.setSelectionRange(0, 4);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(0);
+
+ element.value = ' foobar';
+ element.setSelectionRange(1, 3);
+ ih.unindent();
+ expect(element.value).toBe('foobar');
+ expect(element.cursor()).toBe(0);
+ });
+ });
+
+ describe('several lines', () => {
+ it('when everything is selected', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(0, 27);
+ ih.unindent();
+ expect(element.value).toBe('foo\n bar\nbaz');
+ expect(element.selection()).toEqual([0, 15]);
+ });
+
+ it('when all lines are partially selected', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(5, 26);
+ ih.unindent();
+ expect(element.value).toBe('foo\n bar\nbaz');
+ expect(element.selection()).toEqual([1, 14]);
+ });
+
+ it('when all lines are entirely selected', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(8, 27);
+ ih.unindent();
+ expect(element.value).toBe(' foo\n bar\nbaz');
+ expect(element.selection()).toEqual([8, 19]);
+ });
+
+ it('when some lines are entirely selected', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(8, 27);
+ ih.unindent();
+ expect(element.value).toBe(' foo\n bar\nbaz');
+ expect(element.selection()).toEqual([8, 19]);
+ });
+
+ it('when some lines are partially selected', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(17, 26);
+ ih.unindent();
+ expect(element.value).toBe(' foo\n bar\nbaz');
+ expect(element.selection()).toEqual([13, 18]);
+ });
+
+ it('when some lines are partially selected within their indents', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(10, 22);
+ ih.unindent();
+ expect(element.value).toBe(' foo\n bar\nbaz');
+ expect(element.selection()).toEqual([8, 16]);
+ });
+ });
+ });
+
+ describe('newline', () => {
+ describe('on a single line', () => {
+ it('auto-indents the new line', () => {
+ element.value = 'foo\n bar\n baz\n qux';
+
+ element.setCursor(3);
+ ih.newline();
+ expect(element.value).toBe('foo\n\n bar\n baz\n qux');
+ expect(element.cursor()).toBe(4);
+
+ element.setCursor(9);
+ ih.newline();
+ expect(element.value).toBe('foo\n\n bar\n \n baz\n qux');
+ expect(element.cursor()).toBe(11);
+
+ element.setCursor(19);
+ ih.newline();
+ expect(element.value).toBe('foo\n\n bar\n \n baz\n \n qux');
+ expect(element.cursor()).toBe(24);
+
+ element.setCursor(36);
+ ih.newline();
+ expect(element.value).toBe('foo\n\n bar\n \n baz\n \n qux\n ');
+ expect(element.cursor()).toBe(45);
+ });
+
+ it('splits a line and auto-indents', () => {
+ element.value = ' foobar';
+ element.setCursor(7);
+ ih.newline();
+ expect(element.value).toBe(' foo\n bar');
+ expect(element.cursor()).toBe(12);
+ });
+
+ it('replaces selection with an indented newline', () => {
+ element.value = ' foobarbaz';
+ element.setSelectionRange(7, 10);
+ ih.newline();
+ expect(element.value).toBe(' foo\n baz');
+ expect(element.cursor()).toBe(12);
+ });
+ });
+
+ it('on several lines.replaces selection with indented newline', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setSelectionRange(4, 17);
+ ih.newline();
+ expect(element.value).toBe(' fo\n az');
+ expect(element.cursor()).toBe(7);
+ });
+ });
+
+ describe('backspace', () => {
+ let event;
+
+ // This suite tests only the special indent-removing behaviour of the
+ // backspace() method, since non-special cases are handled natively as a
+ // backspace keypress.
+
+ beforeEach(() => {
+ event = { preventDefault: jest.fn() };
+ });
+
+ describe('on a single line', () => {
+ it('does nothing special if in the line text', () => {
+ element.value = ' foobar';
+ element.setCursor(7);
+ ih.backspace(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('does nothing special if after a non-leading indent', () => {
+ element.value = ' foo bar';
+ element.setCursor(11);
+ ih.backspace(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('deletes one leading indent', () => {
+ element.value = ' foo';
+ element.setCursor(8);
+ ih.backspace(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(element.value).toBe(' foo');
+ expect(element.cursor()).toBe(4);
+ });
+
+ it('does nothing if cursor is inside the leading indent', () => {
+ element.value = ' foo';
+ element.setCursor(4);
+ ih.backspace(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('does nothing if cursor is at the start of the line', () => {
+ element.value = ' foo';
+ element.setCursor(0);
+ ih.backspace(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('deletes one partial indent', () => {
+ element.value = ' foo';
+ element.setCursor(6);
+ ih.backspace(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(element.value).toBe(' foo');
+ expect(element.cursor()).toBe(4);
+ });
+
+ it('deletes indents sequentially', () => {
+ element.value = ' foo';
+ element.setCursor(10);
+ ih.backspace(event);
+ ih.backspace(event);
+ ih.backspace(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(element.value).toBe('foo');
+ expect(element.cursor()).toBe(0);
+ });
+ });
+
+ describe('on several lines', () => {
+ it('deletes indent only on its own line', () => {
+ element.value = ' foo\n bar\n baz';
+ element.setCursor(16);
+ ih.backspace(event);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(element.value).toBe(' foo\n bar\n baz');
+ expect(element.cursor()).toBe(12);
+ });
+
+ it('has no special behaviour with any range selection', () => {
+ const text = ' foo\n bar\n baz';
+ for (let start = 0; start < text.length; start += 1) {
+ for (let end = start + 1; end < text.length; end += 1) {
+ element.value = text;
+ element.setSelectionRange(start, end);
+ ih.backspace(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+
+ // Ensure that the backspace() method doesn't change state
+ // In reality, these two statements won't hold because the browser
+ // will natively process the backspace event.
+ expect(element.value).toBe(text);
+ expect(element.selection()).toEqual([start, end]);
+ }
+ }
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
new file mode 100644
index 00000000000..e3d3b82d2f3
--- /dev/null
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -0,0 +1,180 @@
+import * as cu from '~/lib/utils/common_utils';
+
+const CMD_ENTITY = '&#8984;';
+
+// Redefine `navigator.platform` because it's unsettable by default in JSDOM.
+let platform;
+Object.defineProperty(navigator, 'platform', {
+ configurable: true,
+ get: () => platform,
+ set: val => {
+ platform = val;
+ },
+});
+
+describe('common_utils', () => {
+ describe('platform leader key helpers', () => {
+ const CTRL_EVENT = { ctrlKey: true };
+ const META_EVENT = { metaKey: true };
+ const BOTH_EVENT = { ctrlKey: true, metaKey: true };
+
+ it('should return "ctrl" if navigator.platform is unset', () => {
+ expect(cu.getPlatformLeaderKey()).toBe('ctrl');
+ expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl');
+ expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true);
+ expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false);
+ expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true);
+ });
+
+ it('should return "meta" on MacOS', () => {
+ navigator.platform = 'MacIntel';
+ expect(cu.getPlatformLeaderKey()).toBe('meta');
+ expect(cu.getPlatformLeaderKeyHTML()).toBe(CMD_ENTITY);
+ expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(false);
+ expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(true);
+ expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true);
+ });
+
+ it('should return "ctrl" on Linux', () => {
+ navigator.platform = 'Linux is great';
+ expect(cu.getPlatformLeaderKey()).toBe('ctrl');
+ expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl');
+ expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true);
+ expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false);
+ expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true);
+ });
+
+ it('should return "ctrl" on Windows', () => {
+ navigator.platform = 'Win32';
+ expect(cu.getPlatformLeaderKey()).toBe('ctrl');
+ expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl');
+ expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true);
+ expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false);
+ expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true);
+ });
+ });
+
+ describe('keystroke', () => {
+ const CODE_BACKSPACE = 8;
+ const CODE_TAB = 9;
+ const CODE_ENTER = 13;
+ const CODE_SPACE = 32;
+ const CODE_4 = 52;
+ const CODE_F = 70;
+ const CODE_Z = 90;
+
+ // Helper function that quickly creates KeyboardEvents
+ const k = (code, modifiers = '') => ({
+ keyCode: code,
+ which: code,
+ altKey: modifiers.includes('a'),
+ ctrlKey: modifiers.includes('c'),
+ metaKey: modifiers.includes('m'),
+ shiftKey: modifiers.includes('s'),
+ });
+
+ const EV_F = k(CODE_F);
+ const EV_ALT_F = k(CODE_F, 'a');
+ const EV_CONTROL_F = k(CODE_F, 'c');
+ const EV_META_F = k(CODE_F, 'm');
+ const EV_SHIFT_F = k(CODE_F, 's');
+ const EV_CONTROL_SHIFT_F = k(CODE_F, 'cs');
+ const EV_ALL_F = k(CODE_F, 'scma');
+ const EV_ENTER = k(CODE_ENTER);
+ const EV_TAB = k(CODE_TAB);
+ const EV_SPACE = k(CODE_SPACE);
+ const EV_BACKSPACE = k(CODE_BACKSPACE);
+ const EV_4 = k(CODE_4);
+ const EV_$ = k(CODE_4, 's');
+
+ const { keystroke } = cu;
+
+ it('short-circuits with bad arguments', () => {
+ expect(keystroke()).toBe(false);
+ expect(keystroke({})).toBe(false);
+ });
+
+ it('handles keystrokes using key codes', () => {
+ // Test a letter key with modifiers
+ expect(keystroke(EV_F, CODE_F)).toBe(true);
+ expect(keystroke(EV_F, CODE_F, '')).toBe(true);
+ expect(keystroke(EV_ALT_F, CODE_F, 'a')).toBe(true);
+ expect(keystroke(EV_CONTROL_F, CODE_F, 'c')).toBe(true);
+ expect(keystroke(EV_META_F, CODE_F, 'm')).toBe(true);
+ expect(keystroke(EV_SHIFT_F, CODE_F, 's')).toBe(true);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true);
+ expect(keystroke(EV_ALL_F, CODE_F, 'acms')).toBe(true);
+
+ // Test non-letter keys
+ expect(keystroke(EV_TAB, CODE_TAB)).toBe(true);
+ expect(keystroke(EV_ENTER, CODE_ENTER)).toBe(true);
+ expect(keystroke(EV_SPACE, CODE_SPACE)).toBe(true);
+ expect(keystroke(EV_BACKSPACE, CODE_BACKSPACE)).toBe(true);
+
+ // Test a number/symbol key
+ expect(keystroke(EV_4, CODE_4)).toBe(true);
+ expect(keystroke(EV_$, CODE_4, 's')).toBe(true);
+
+ // Test wrong input
+ expect(keystroke(EV_F, CODE_Z)).toBe(false);
+ expect(keystroke(EV_SHIFT_F, CODE_F)).toBe(false);
+ expect(keystroke(EV_SHIFT_F, CODE_F, 'c')).toBe(false);
+ });
+
+ it('is case-insensitive', () => {
+ expect(keystroke(EV_ALL_F, CODE_F, 'ACMS')).toBe(true);
+ });
+
+ it('handles bogus inputs', () => {
+ expect(keystroke(EV_F, 'not a keystroke')).toBe(false);
+ expect(keystroke(EV_F, null)).toBe(false);
+ });
+
+ it('handles exact modifier keys, in any order', () => {
+ // Test permutations of modifiers
+ expect(keystroke(EV_ALL_F, CODE_F, 'acms')).toBe(true);
+ expect(keystroke(EV_ALL_F, CODE_F, 'smca')).toBe(true);
+ expect(keystroke(EV_ALL_F, CODE_F, 'csma')).toBe(true);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'sc')).toBe(true);
+
+ // Test wrong modifiers
+ expect(keystroke(EV_ALL_F, CODE_F, 'smca')).toBe(true);
+ expect(keystroke(EV_ALL_F, CODE_F)).toBe(false);
+ expect(keystroke(EV_ALL_F, CODE_F, '')).toBe(false);
+ expect(keystroke(EV_ALL_F, CODE_F, 'c')).toBe(false);
+ expect(keystroke(EV_ALL_F, CODE_F, 'ca')).toBe(false);
+ expect(keystroke(EV_ALL_F, CODE_F, 'ms')).toBe(false);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'c')).toBe(false);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 's')).toBe(false);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'csa')).toBe(false);
+ expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'm')).toBe(false);
+ expect(keystroke(EV_SHIFT_F, CODE_F, 's')).toBe(true);
+ expect(keystroke(EV_SHIFT_F, CODE_F, 'c')).toBe(false);
+ expect(keystroke(EV_SHIFT_F, CODE_F, 'csm')).toBe(false);
+ });
+
+ it('handles the platform-dependent leader key', () => {
+ navigator.platform = 'Win32';
+ let EV_UNDO = k(CODE_Z, 'c');
+ let EV_REDO = k(CODE_Z, 'cs');
+ expect(keystroke(EV_UNDO, CODE_Z, 'l')).toBe(true);
+ expect(keystroke(EV_UNDO, CODE_Z, 'c')).toBe(true);
+ expect(keystroke(EV_UNDO, CODE_Z, 'm')).toBe(false);
+ expect(keystroke(EV_REDO, CODE_Z, 'sl')).toBe(true);
+ expect(keystroke(EV_REDO, CODE_Z, 'sc')).toBe(true);
+ expect(keystroke(EV_REDO, CODE_Z, 'sm')).toBe(false);
+
+ navigator.platform = 'MacIntel';
+ EV_UNDO = k(CODE_Z, 'm');
+ EV_REDO = k(CODE_Z, 'ms');
+ expect(keystroke(EV_UNDO, CODE_Z, 'l')).toBe(true);
+ expect(keystroke(EV_UNDO, CODE_Z, 'c')).toBe(false);
+ expect(keystroke(EV_UNDO, CODE_Z, 'm')).toBe(true);
+ expect(keystroke(EV_REDO, CODE_Z, 'sl')).toBe(true);
+ expect(keystroke(EV_REDO, CODE_Z, 'sc')).toBe(false);
+ expect(keystroke(EV_REDO, CODE_Z, 'sm')).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/undo_stack_spec.js b/spec/frontend/lib/utils/undo_stack_spec.js
new file mode 100644
index 00000000000..31ad0e77d6f
--- /dev/null
+++ b/spec/frontend/lib/utils/undo_stack_spec.js
@@ -0,0 +1,237 @@
+import UndoStack from '~/lib/utils/undo_stack';
+
+import { isEqual } from 'underscore';
+
+describe('UndoStack', () => {
+ let stack;
+
+ beforeEach(() => {
+ stack = new UndoStack();
+ });
+
+ afterEach(() => {
+ // Make sure there's not pending saves
+ const history = Array.from(stack.history);
+ jest.runAllTimers();
+ expect(stack.history).toEqual(history);
+ });
+
+ it('is blank on construction', () => {
+ expect(stack.isEmpty()).toBe(true);
+ expect(stack.history).toEqual([]);
+ expect(stack.cursor).toBe(-1);
+ expect(stack.canUndo()).toBe(false);
+ expect(stack.canRedo()).toBe(false);
+ });
+
+ it('handles simple undo/redo behaviour', () => {
+ stack.save(10);
+ stack.save(11);
+ stack.save(12);
+
+ expect(stack.history).toEqual([10, 11, 12]);
+ expect(stack.cursor).toBe(2);
+ expect(stack.current()).toBe(12);
+ expect(stack.isEmpty()).toBe(false);
+ expect(stack.canUndo()).toBe(true);
+ expect(stack.canRedo()).toBe(false);
+
+ stack.undo();
+ expect(stack.history).toEqual([10, 11, 12]);
+ expect(stack.current()).toBe(11);
+ expect(stack.canUndo()).toBe(true);
+ expect(stack.canRedo()).toBe(true);
+
+ stack.undo();
+ expect(stack.current()).toBe(10);
+ expect(stack.canUndo()).toBe(false);
+ expect(stack.canRedo()).toBe(true);
+
+ stack.redo();
+ expect(stack.current()).toBe(11);
+
+ stack.redo();
+ expect(stack.current()).toBe(12);
+ expect(stack.isEmpty()).toBe(false);
+ expect(stack.canUndo()).toBe(true);
+ expect(stack.canRedo()).toBe(false);
+
+ // Saving should clear the redo stack
+ stack.undo();
+ stack.save(13);
+ expect(stack.history).toEqual([10, 11, 13]);
+ expect(stack.current()).toBe(13);
+ });
+
+ it('clear() should clear the undo history', () => {
+ stack.save(0);
+ stack.save(1);
+ stack.save(2);
+ stack.clear();
+ expect(stack.history).toEqual([]);
+ expect(stack.current()).toBeUndefined();
+ });
+
+ it('undo and redo are no-ops if unavailable', () => {
+ stack.save(10);
+ expect(stack.canRedo()).toBe(false);
+ expect(stack.canUndo()).toBe(false);
+
+ stack.save(11);
+ expect(stack.canRedo()).toBe(false);
+ expect(stack.canUndo()).toBe(true);
+
+ expect(stack.redo()).toBeUndefined();
+ expect(stack.history).toEqual([10, 11]);
+ expect(stack.current()).toBe(11);
+ expect(stack.canRedo()).toBe(false);
+ expect(stack.canUndo()).toBe(true);
+
+ expect(stack.undo()).toBe(10);
+ expect(stack.undo()).toBeUndefined();
+ expect(stack.history).toEqual([10, 11]);
+ expect(stack.current()).toBe(10);
+ expect(stack.canRedo()).toBe(true);
+ expect(stack.canUndo()).toBe(false);
+ });
+
+ it('should not save a duplicate state', () => {
+ stack.save(10);
+ stack.save(11);
+ stack.save(11);
+ stack.save(10);
+ stack.save(10);
+
+ expect(stack.history).toEqual([10, 11, 10]);
+ });
+
+ it('uses the === operator to detect duplicates', () => {
+ stack.save(10);
+ stack.save(10);
+ expect(stack.history).toEqual([10]);
+
+ // eslint-disable-next-line eqeqeq
+ expect(2 == '2' && '2' == 2).toBe(true);
+ stack.clear();
+ stack.save(2);
+ stack.save(2);
+ stack.save('2');
+ stack.save('2');
+ stack.save(2);
+ expect(stack.history).toEqual([2, '2', 2]);
+
+ const obj = {};
+ stack.clear();
+ stack.save(obj);
+ stack.save(obj);
+ stack.save({});
+ stack.save({});
+ expect(stack.history).toEqual([{}, {}, {}]);
+ });
+
+ it('should allow custom comparators', () => {
+ stack.comparator = isEqual;
+ const obj = {};
+ stack.clear();
+ stack.save(obj);
+ stack.save(obj);
+ stack.save({});
+ stack.save({});
+ expect(stack.history).toEqual([{}]);
+ });
+
+ it('should enforce a max number of undo states', () => {
+ // Try 2000 saves. Only the last 1000 should be preserved.
+ const sequence = Array(2000)
+ .fill(0)
+ .map((el, i) => i);
+ sequence.forEach(stack.save.bind(stack));
+ expect(stack.history.length).toBe(1000);
+ expect(stack.history).toEqual(sequence.slice(1000));
+ expect(stack.current()).toBe(1999);
+ expect(stack.canUndo()).toBe(true);
+ expect(stack.canRedo()).toBe(false);
+
+ // Saving drops the oldest elements from the stack
+ stack.save('end');
+ expect(stack.history.length).toBe(1000);
+ expect(stack.current()).toBe('end');
+ expect(stack.history).toEqual([...sequence.slice(1001), 'end']);
+
+ // If states were undone but the history is full, can still add.
+ stack.undo();
+ stack.undo();
+ expect(stack.current()).toBe(1998);
+ stack.save(3000);
+ expect(stack.history.length).toBe(999);
+ // should be [1001, 1002, ..., 1998, 3000]
+ expect(stack.history).toEqual([...sequence.slice(1001, 1999), 3000]);
+
+ // Try a different max length
+ stack = new UndoStack(2);
+ stack.save(0);
+ expect(stack.history).toEqual([0]);
+ stack.save(1);
+ expect(stack.history).toEqual([0, 1]);
+ stack.save(2);
+ expect(stack.history).toEqual([1, 2]);
+ });
+
+ describe('scheduled saves', () => {
+ it('should work', () => {
+ // Schedules 1000 ms ahead by default
+ stack.save(0);
+ stack.scheduleSave(1);
+ expect(stack.history).toEqual([0]);
+ jest.advanceTimersByTime(999);
+ expect(stack.history).toEqual([0]);
+ jest.advanceTimersByTime(1);
+ expect(stack.history).toEqual([0, 1]);
+ });
+
+ it('should have an adjustable delay', () => {
+ stack.scheduleSave(2, 100);
+ jest.advanceTimersByTime(100);
+ expect(stack.history).toEqual([2]);
+ });
+
+ it('should cancel previous scheduled saves', () => {
+ stack.scheduleSave(3);
+ jest.advanceTimersByTime(100);
+ stack.scheduleSave(4);
+ jest.runAllTimers();
+ expect(stack.history).toEqual([4]);
+ });
+
+ it('should be canceled by explicit saves', () => {
+ stack.scheduleSave(5);
+ stack.save(6);
+ jest.runAllTimers();
+ expect(stack.history).toEqual([6]);
+ });
+
+ it('should be canceled by undos and redos', () => {
+ stack.save(1);
+ stack.save(2);
+ stack.scheduleSave(3);
+ stack.undo();
+ jest.runAllTimers();
+ expect(stack.history).toEqual([1, 2]);
+ expect(stack.current()).toBe(1);
+
+ stack.scheduleSave(4);
+ stack.redo();
+ jest.runAllTimers();
+ expect(stack.history).toEqual([1, 2]);
+ expect(stack.current()).toBe(2);
+ });
+
+ it('should be persisted immediately with saveNow()', () => {
+ stack.scheduleSave(7);
+ stack.scheduleSave(8);
+ stack.saveNow();
+ jest.runAllTimers();
+ expect(stack.history).toEqual([8]);
+ });
+ });
+});
diff --git a/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js b/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js
new file mode 100644
index 00000000000..7b8df03d3c3
--- /dev/null
+++ b/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js
@@ -0,0 +1,61 @@
+import initGkeNamespace from '~/projects/gke_cluster_namespace';
+
+describe('GKE cluster namespace', () => {
+ const changeEvent = new Event('change');
+ const isHidden = el => el.classList.contains('hidden');
+ const hasDisabledInput = el => el.querySelector('input').disabled;
+
+ let glManagedCheckbox;
+ let selfManaged;
+ let glManaged;
+
+ beforeEach(() => {
+ setFixtures(`
+ <input class="js-gl-managed" type="checkbox" value="1" checked />
+ <div class="js-namespace">
+ <input type="text" />
+ </div>
+ <div class="js-namespace-prefixed">
+ <input type="text" />
+ </div>
+ `);
+
+ glManagedCheckbox = document.querySelector('.js-gl-managed');
+ selfManaged = document.querySelector('.js-namespace');
+ glManaged = document.querySelector('.js-namespace-prefixed');
+
+ initGkeNamespace();
+ });
+
+ describe('GKE cluster namespace toggles', () => {
+ it('initially displays the GitLab-managed label and input', () => {
+ expect(isHidden(glManaged)).toEqual(false);
+ expect(hasDisabledInput(glManaged)).toEqual(false);
+
+ expect(isHidden(selfManaged)).toEqual(true);
+ expect(hasDisabledInput(selfManaged)).toEqual(true);
+ });
+
+ it('displays the self-managed label and input when the Gitlab-managed checkbox is unchecked', () => {
+ glManagedCheckbox.checked = false;
+ glManagedCheckbox.dispatchEvent(changeEvent);
+
+ expect(isHidden(glManaged)).toEqual(true);
+ expect(hasDisabledInput(glManaged)).toEqual(true);
+
+ expect(isHidden(selfManaged)).toEqual(false);
+ expect(hasDisabledInput(selfManaged)).toEqual(false);
+ });
+
+ it('displays the GitLab-managed label and input when the Gitlab-managed checkbox is checked', () => {
+ glManagedCheckbox.checked = true;
+ glManagedCheckbox.dispatchEvent(changeEvent);
+
+ expect(isHidden(glManaged)).toEqual(false);
+ expect(hasDisabledInput(glManaged)).toEqual(false);
+
+ expect(isHidden(selfManaged)).toEqual(true);
+ expect(hasDisabledInput(selfManaged)).toEqual(true);
+ });
+ });
+});
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index 20e197e9f73..47591445fc0 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Resolvers::NamespaceProjectsResolver, :nested_groups do
+describe Resolvers::NamespaceProjectsResolver do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/helpers/auto_devops_helper_spec.rb b/spec/helpers/auto_devops_helper_spec.rb
index d2540696b17..e80388f9ea7 100644
--- a/spec/helpers/auto_devops_helper_spec.rb
+++ b/spec/helpers/auto_devops_helper_spec.rb
@@ -118,7 +118,7 @@ describe AutoDevopsHelper do
it { is_expected.to eq('instance enabled') }
end
- context 'with groups', :nested_groups do
+ context 'with groups' do
before do
receiver.update(parent: parent)
end
@@ -170,7 +170,7 @@ describe AutoDevopsHelper do
it { is_expected.to eq('instance enabled') }
end
- context 'with groups', :nested_groups do
+ context 'with groups' do
let(:receiver) { create(:project, :repository, namespace: group) }
before do
@@ -203,7 +203,7 @@ describe AutoDevopsHelper do
it { is_expected.to be_nil }
end
- context 'with groups', :nested_groups do
+ context 'with groups' do
let(:receiver) { create(:project, :repository, namespace: group) }
context 'when auto devops is disabled on group level' do
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 1763c46389a..037b16c90ed 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -86,7 +86,7 @@ describe GroupsHelper do
end
end
- describe 'group_title', :nested_groups do
+ describe 'group_title' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -99,7 +99,7 @@ describe GroupsHelper do
end
# rubocop:disable Layout/SpaceBeforeComma
- describe '#share_with_group_lock_help_text', :nested_groups do
+ describe '#share_with_group_lock_help_text' do
let!(:root_group) { create(:group) }
let!(:subgroup) { create(:group, parent: root_group) }
let!(:sub_subgroup) { create(:group, parent: subgroup) }
@@ -230,7 +230,7 @@ describe GroupsHelper do
end
end
- describe 'parent_group_options', :nested_groups do
+ describe 'parent_group_options' do
let(:current_user) { create(:user) }
let(:group) { create(:group, name: 'group') }
let(:group2) { create(:group, name: 'group2') }
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 601f864ef36..e38513f6d94 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe NamespacesHelper, :postgresql do
+describe NamespacesHelper do
let!(:admin) { create(:admin) }
let!(:admin_project_creation_level) { nil }
let!(:admin_group) do
@@ -109,7 +109,7 @@ describe NamespacesHelper, :postgresql do
expect(options).to include(user_group.name)
end
- context 'when nested groups are available', :nested_groups do
+ context 'when nested groups are available' do
it 'includes groups nested in groups the user can administer' do
allow(helper).to receive(:current_user).and_return(user)
child_group = create(:group, :private, parent: user_group)
diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb
index b922b910c89..ab4ef899119 100644
--- a/spec/helpers/submodule_helper_spec.rb
+++ b/spec/helpers/submodule_helper_spec.rb
@@ -229,6 +229,19 @@ describe SubmoduleHelper do
end
end
end
+
+ context 'unknown submodule' do
+ before do
+ # When there is no `.gitmodules` file, or if `.gitmodules` does not
+ # know the submodule at the specified path,
+ # `Repository#submodule_url_for` returns `nil`
+ stub_url(nil)
+ end
+
+ it 'returns no links' do
+ expect(subject).to eq([nil, nil])
+ end
+ end
end
context 'as view helpers in view context' do
diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js
index 2f72c9ed89d..2fa807657de 100644
--- a/spec/javascripts/badges/components/badge_list_spec.js
+++ b/spec/javascripts/badges/components/badge_list_spec.js
@@ -60,7 +60,7 @@ describe('BadgeList component', () => {
Vue.nextTick()
.then(() => {
- const loadingIcon = vm.$el.querySelector('.spinner');
+ const loadingIcon = vm.$el.querySelector('.gl-spinner');
expect(loadingIcon).toBeVisible();
})
diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js
index 4e4d1ae2e99..c82a03a628a 100644
--- a/spec/javascripts/badges/components/badge_spec.js
+++ b/spec/javascripts/badges/components/badge_spec.js
@@ -15,7 +15,7 @@ describe('Badge component', () => {
const buttons = vm.$el.querySelectorAll('button');
return {
badgeImage: vm.$el.querySelector('img.project-badge'),
- loadingIcon: vm.$el.querySelector('.spinner'),
+ loadingIcon: vm.$el.querySelector('.gl-spinner'),
reloadButton: buttons[buttons.length - 1],
};
};
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 9c9b435d7fd..6774a46ed58 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -148,7 +148,7 @@ describe('Board list component', () => {
component.list.loadingMore = true;
Vue.nextTick(() => {
- expect(component.$el.querySelector('.board-list-count .spinner')).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count .gl-spinner')).not.toBeNull();
done();
});
diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js
index 7b9b8d2b039..e7675669f7a 100644
--- a/spec/javascripts/registry/components/app_spec.js
+++ b/spec/javascripts/registry/components/app_spec.js
@@ -106,7 +106,7 @@ describe('Registry List', () => {
it('should render a loading spinner', done => {
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.spinner')).not.toBe(null);
+ expect(vm.$el.querySelector('.gl-spinner')).not.toBe(null);
done();
});
});
diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
index a17494966a3..1f1e626ed33 100644
--- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
+++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
@@ -34,7 +34,7 @@ describe('Grouped Test Reports App', () => {
it('renders success summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained no changed test results out of 11 total tests',
);
@@ -61,7 +61,7 @@ describe('Grouped Test Reports App', () => {
it('renders success summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
@@ -81,7 +81,7 @@ describe('Grouped Test Reports App', () => {
it('renders failed summary text + new badge', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.spinner')).toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed test results out of 11 total tests',
);
@@ -109,7 +109,7 @@ describe('Grouped Test Reports App', () => {
it('renders summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.spinner')).toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed test results and 2 fixed test results out of 11 total tests',
);
@@ -137,7 +137,7 @@ describe('Grouped Test Reports App', () => {
it('renders summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.spinner')).toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 fixed test results out of 11 total tests',
);
@@ -190,7 +190,7 @@ describe('Grouped Test Reports App', () => {
});
it('renders loading summary text with loading icon', done => {
- expect(vm.$el.querySelector('.spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
index f622f52a7b9..5aac37d28df 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
@@ -18,7 +18,7 @@ describe('MR widget status icon component', () => {
it('renders loading icon', () => {
vm = mountComponent(Component, { status: 'loading' });
- expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner');
+ expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
index d93badf8cd3..55a11a72551 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -38,7 +38,9 @@ describe('MRWidgetAutoMergeFailed', () => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- expect(vm.$el.querySelector('button .loading-container span').classList).toContain('spinner');
+ expect(vm.$el.querySelector('button .loading-container span').classList).toContain(
+ 'gl-spinner',
+ );
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
index 96e512d222a..70c70eca746 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
@@ -20,7 +20,7 @@ describe('MRWidgetChecking', () => {
});
it('renders loading icon', () => {
- expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner');
+ expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner');
});
it('renders information about merging', () => {
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
index 5bea8c43da3..1f61e19fa84 100644
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -72,7 +72,7 @@ describe('File Icon component', () => {
const { classList } = vm.$el.querySelector('.loading-container span');
- expect(classList.contains('spinner')).toEqual(true);
+ expect(classList.contains('gl-spinner')).toEqual(true);
});
it('should add a special class and a size class', () => {
diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
index a9c1a67b39b..2b059e5e9f4 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -88,7 +88,7 @@ describe('Header CI Component', () => {
vm.actions[0].isLoading = true;
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .spinner').getAttribute('style')).toBeFalsy();
+ expect(vm.$el.querySelector('.btn .gl-spinner').getAttribute('style')).toBeFalsy();
done();
});
});
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index f0a5dc8d0d7..91edadfa234 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -363,7 +363,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a')).to be_empty
end
- it 'supports parent group references', :nested_groups do
+ it 'supports parent group references' do
milestone.update!(group: parent_group)
doc = reference_filter("See #{reference}")
@@ -396,7 +396,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
context 'when group milestone' do
let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) }
- context 'for subgroups', :nested_groups do
+ context 'for subgroups' do
let(:sub_group) { create(:group, parent: group) }
let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) }
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index 3df33f48adb..ce06377bbbf 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -6,6 +6,42 @@ describe ContainerRegistry::Client do
let(:options) { { token: token } }
let(:client) { described_class.new("http://container-registry", options) }
+ shared_examples '#repository_manifest' do |manifest_type|
+ let(:manifest) do
+ {
+ "schemaVersion" => 2,
+ "config" => {
+ "mediaType" => manifest_type,
+ "digest" =>
+ "sha256:4a3ef0786dd241be6000311e1503869b320be433b9cba84cfafeb512d1720c95",
+ "size" => 6608
+ },
+ "layers" => [
+ {
+ "mediaType" => manifest_type,
+ "digest" =>
+ "sha256:83ef92b73cf4595aa7fe214ec6747228283d585f373d8f6bc08d66bebab531b7",
+ "size" => 2828661
+ }
+ ]
+ }
+ end
+
+ it 'GET /v2/:name/manifests/mytag' do
+ stub_request(:get, "http://container-registry/v2/group/test/manifests/mytag")
+ .with(headers: {
+ 'Accept' => described_class::ACCEPTED_TYPES.join(', '),
+ 'Authorization' => "bearer #{token}"
+ })
+ .to_return(status: 200, body: manifest.to_json, headers: { content_type: manifest_type })
+
+ expect(client.repository_manifest('group/test', 'mytag')).to eq(manifest)
+ end
+ end
+
+ it_behaves_like '#repository_manifest', described_class::DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE
+ it_behaves_like '#repository_manifest', described_class::OCI_MANIFEST_V1_TYPE
+
describe '#blob' do
it 'GET /v2/:name/blobs/:digest' do
stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index cb4ae3be525..65090f32f66 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -9,7 +9,7 @@ describe ContainerRegistry::Tag do
end
let(:headers) do
- { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' }
+ { 'Accept' => ContainerRegistry::Client::ACCEPTED_TYPES.join(', ') }
end
let(:tag) { described_class.new(repository, 'tag') }
diff --git a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
index 40001cea22e..a5436149818 100644
--- a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
@@ -40,7 +40,11 @@ describe Gitlab::Auth::OAuth::AuthHash do
last_name: last_name_ascii,
name: name_ascii,
nickname: nickname_ascii,
- uid: uid_ascii
+ uid: uid_ascii,
+ address: {
+ locality: 'some locality',
+ country: 'some country'
+ }
}
end
@@ -51,6 +55,7 @@ describe Gitlab::Auth::OAuth::AuthHash do
it { expect(auth_hash.username).to eql nickname_utf8 }
it { expect(auth_hash.name).to eql name_utf8 }
it { expect(auth_hash.password).not_to be_empty }
+ it { expect(auth_hash.location).to eq 'some locality, some country' }
end
context 'email not provided' do
diff --git a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
index 20af63bc6c8..84ccd57a50b 100644
--- a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
+++ b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb
@@ -75,7 +75,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch
create_resource(target_type, 1, 2)
end
- it 'ignores label links referencing ancestor group labels', :nested_groups do
+ it 'ignores label links referencing ancestor group labels' do
labels_table.create(id: 4, title: 'bug', color: 'red', project_id: 2, type: 'ProjectLabel')
label_links_table.create(label_id: 4, target_type: target_type, target_id: 1)
link = label_links_table.create(label_id: 1, target_type: target_type, target_id: 1)
@@ -85,7 +85,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch
expect(link.reload.label_id).to eq(1)
end
- it 'checks also issues and MRs in subgroups', :nested_groups do
+ it 'checks also issues and MRs in subgroups' do
link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1)
subject.perform(1, 100)
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index 0dee683350f..0d2074eed22 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -114,7 +114,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
it 'does not drop the temporary tracking table after processing the batch, if there are still untracked rows' do
subject.perform(1, untracked_files_for_uploads.last.id - 1)
- expect(ActiveRecord::Base.connection.data_source_exists?(:untracked_files_for_uploads)).to be_truthy
+ expect(ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)).to be_truthy
end
it 'drops the temporary tracking table after processing the batch, if there are no untracked rows left' do
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index f4759b69538..ebd3c28f130 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -103,7 +103,7 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
end
end
- context 'with subgroups', :nested_groups do
+ context 'with subgroups' do
let(:project_path) { 'a-group/a-sub-group/a-project' }
let(:existing_group) do
@@ -188,20 +188,6 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do
end
end
- context 'when subgroups are not available' do
- let(:project_path) { 'a-group/a-sub-group/a-project' }
-
- before do
- expect(Group).to receive(:supports_nested_objects?) { false }
- end
-
- describe '#create_project_if_needed' do
- it 'raises an error' do
- expect { importer.create_project_if_needed }.to raise_error('Nested groups are not supported on MySQL')
- end
- end
- end
-
def prepare_repository(project_path, source_project)
repo_path = File.join(base_dir, project_path)
diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
index a528707c9dc..959f3fdc289 100644
--- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
+++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do
subject { described_class.new(models).count }
- describe '#count', :postgresql do
+ describe '#count' do
let(:models) { [Project, Identity] }
context 'when reltuples is up to date' do
diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
index a57f033b5ed..cc9d778e579 100644
--- a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
+++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do
subject { strategy.count }
- describe '#count', :postgresql do
+ describe '#count' do
let(:estimates) do
{
Project => threshold + 1,
diff --git a/spec/lib/gitlab/database/grant_spec.rb b/spec/lib/gitlab/database/grant_spec.rb
index 5ebf3f399b6..9e62dc14d77 100644
--- a/spec/lib/gitlab/database/grant_spec.rb
+++ b/spec/lib/gitlab/database/grant_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Database::Grant do
expect(described_class.create_and_execute_trigger?('users')).to eq(true)
end
- it 'returns false when the user can not create and/or execute a trigger', :postgresql do
+ it 'returns false when the user can not create and/or execute a trigger' do
# In case of MySQL the user may have SUPER permissions, making it
# impossible to have `false` returned when running tests; hence we only
# run these tests on PostgreSQL.
diff --git a/spec/lib/gitlab/group_search_results_spec.rb b/spec/lib/gitlab/group_search_results_spec.rb
index 2734fcef0a0..53a91a35ec9 100644
--- a/spec/lib/gitlab/group_search_results_spec.rb
+++ b/spec/lib/gitlab/group_search_results_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
- it 'returns the user belonging to the subgroup matching the search query', :nested_groups do
+ it 'returns the user belonging to the subgroup matching the search query' do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
@@ -32,7 +32,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
- it 'returns the user belonging to the parent group matching the search query', :nested_groups do
+ it 'returns the user belonging to the parent group matching the search query' do
user1 = create(:user, username: 'gob_bluth')
parent_group = create(:group, children: [group])
create(:group_member, :developer, user: user1, group: parent_group)
@@ -44,7 +44,7 @@ describe Gitlab::GroupSearchResults do
expect(result).to eq [user1]
end
- it 'does not return the user belonging to the private subgroup', :nested_groups do
+ it 'does not return the user belonging to the private subgroup' do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, :private, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb
index ab305fb2316..ded93e23c08 100644
--- a/spec/lib/gitlab/manifest_import/manifest_spec.rb
+++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ManifestImport::Manifest, :postgresql do
+describe Gitlab::ManifestImport::Manifest do
let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
let(:manifest) { described_class.new(file) }
diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
index 1d01d437535..a7487972f51 100644
--- a/spec/lib/gitlab/manifest_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
+describe Gitlab::ManifestImport::ProjectCreator do
let(:group) { create(:group) }
let(:user) { create(:user) }
let(:repository) do
diff --git a/spec/lib/gitlab/object_hierarchy_spec.rb b/spec/lib/gitlab/object_hierarchy_spec.rb
index e6e9ae3223e..bfd456cdd7e 100644
--- a/spec/lib/gitlab/object_hierarchy_spec.rb
+++ b/spec/lib/gitlab/object_hierarchy_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::ObjectHierarchy, :postgresql do
+describe Gitlab::ObjectHierarchy do
let!(:parent) { create(:group) }
let!(:child1) { create(:group, parent: parent) }
let!(:child2) { create(:group, parent: child1) }
diff --git a/spec/lib/gitlab/performance_bar_spec.rb b/spec/lib/gitlab/performance_bar_spec.rb
index ee3c571c9c0..71c109db1f1 100644
--- a/spec/lib/gitlab/performance_bar_spec.rb
+++ b/spec/lib/gitlab/performance_bar_spec.rb
@@ -96,7 +96,7 @@ describe Gitlab::PerformanceBar do
end
end
- context 'when allowed group is nested', :nested_groups do
+ context 'when allowed group is nested' do
let!(:nested_my_group) { create(:group, parent: create(:group, path: 'my-org'), path: 'my-group') }
before do
@@ -110,7 +110,7 @@ describe Gitlab::PerformanceBar do
end
end
- context 'when a nested group has the same path', :nested_groups do
+ context 'when a nested group has the same path' do
before do
create(:group, :nested, path: 'my-group').add_developer(user)
end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index bd0bc2c9044..75e2d5e1319 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -20,13 +20,7 @@ describe Gitlab::ProjectAuthorizations do
end
let(:authorizations) do
- klass = if Group.supports_nested_objects?
- Gitlab::ProjectAuthorizations::WithNestedGroups
- else
- Gitlab::ProjectAuthorizations::WithoutNestedGroups
- end
-
- klass.new(user).calculate
+ described_class.new(user).calculate
end
it 'returns the correct number of authorizations' do
@@ -46,28 +40,26 @@ describe Gitlab::ProjectAuthorizations do
expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER)
end
- if Group.supports_nested_objects?
- context 'with nested groups' do
- let!(:nested_group) { create(:group, parent: group) }
- let!(:nested_project) { create(:project, namespace: nested_group) }
+ context 'with nested groups' do
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:nested_project) { create(:project, namespace: nested_group) }
- it 'includes nested groups' do
- expect(authorizations.pluck(:project_id)).to include(nested_project.id)
- end
+ it 'includes nested groups' do
+ expect(authorizations.pluck(:project_id)).to include(nested_project.id)
+ end
- it 'inherits access levels when the user is not a member of a nested group' do
- mapping = map_access_levels(authorizations)
+ it 'inherits access levels when the user is not a member of a nested group' do
+ mapping = map_access_levels(authorizations)
- expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER)
- end
+ expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER)
+ end
- it 'uses the greatest access level when a user is a member of a nested group' do
- nested_group.add_maintainer(user)
+ it 'uses the greatest access level when a user is a member of a nested group' do
+ nested_group.add_maintainer(user)
- mapping = map_access_levels(authorizations)
+ mapping = map_access_levels(authorizations)
- expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
- end
+ expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER)
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
index 1a5a38b5d99..b451844f06c 100644
--- a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
@@ -45,6 +45,9 @@ describe Gitlab::SidekiqMiddleware::MemoryKiller do
expect(subject).to receive(:sleep).with(10).ordered
expect(Process).to receive(:kill).with('SIGKILL', pid).ordered
+ expect(Sidekiq.logger)
+ .to receive(:warn).with(class: 'TestWorker', message: anything, pid: pid, signal: anything).at_least(:once)
+
run
end
diff --git a/spec/lib/gitlab/sql/cte_spec.rb b/spec/lib/gitlab/sql/cte_spec.rb
index d6763c7b2e1..5d2164491b5 100644
--- a/spec/lib/gitlab/sql/cte_spec.rb
+++ b/spec/lib/gitlab/sql/cte_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::SQL::CTE, :postgresql do
+describe Gitlab::SQL::CTE do
describe '#to_arel' do
it 'generates an Arel relation for the CTE body' do
relation = User.where(id: 1)
diff --git a/spec/lib/gitlab/sql/recursive_cte_spec.rb b/spec/lib/gitlab/sql/recursive_cte_spec.rb
index 7fe39dd5a96..407a4d8a247 100644
--- a/spec/lib/gitlab/sql/recursive_cte_spec.rb
+++ b/spec/lib/gitlab/sql/recursive_cte_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::SQL::RecursiveCTE, :postgresql do
+describe Gitlab::SQL::RecursiveCTE do
let(:cte) { described_class.new(:cte_name) }
describe '#to_arel' do
diff --git a/spec/lib/gitlab/submodule_links_spec.rb b/spec/lib/gitlab/submodule_links_spec.rb
new file mode 100644
index 00000000000..a84602cd07d
--- /dev/null
+++ b/spec/lib/gitlab/submodule_links_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SubmoduleLinks do
+ let(:submodule_item) { double(id: 'hash', path: 'gitlab-ce') }
+ let(:repo) { double }
+ let(:links) { described_class.new(repo) }
+
+ describe '#for' do
+ subject { links.for(submodule_item, 'ref') }
+
+ context 'when there is no .gitmodules file' do
+ before do
+ stub_urls(nil)
+ end
+
+ it 'returns no links' do
+ expect(subject).to eq([nil, nil])
+ end
+ end
+
+ context 'when the submodule is unknown' do
+ before do
+ stub_urls({ 'path' => 'url' })
+ end
+
+ it 'returns no links' do
+ expect(subject).to eq([nil, nil])
+ end
+ end
+
+ context 'when the submodule is known' do
+ before do
+ stub_urls({ 'gitlab-ce' => 'git@gitlab.com:gitlab-org/gitlab-ce.git' })
+ end
+
+ it 'returns links' do
+ expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash'])
+ end
+ end
+ end
+
+ def stub_urls(urls)
+ allow(repo).to receive(:submodule_urls_for).and_return(urls)
+ end
+end
diff --git a/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb
index 41afbbb191c..4e8ae35187e 100644
--- a/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb
@@ -2,68 +2,13 @@
require 'spec_helper'
-describe Gitlab::UsageDataCounters::WikiPageCounter, :clean_gitlab_redis_shared_state do
- shared_examples :wiki_page_event do |event|
- describe ".count(#{event})" do
- it "increments the wiki page #{event} counter by 1" do
- expect do
- described_class.count(event)
- end.to change { described_class.read(event) }.by 1
- end
- end
-
- describe ".read(#{event})" do
- event_count = 5
-
- it "returns the total number of #{event} events" do
- event_count.times do
- described_class.count(event)
- end
-
- expect(described_class.read(event)).to eq(event_count)
- end
- end
- end
-
- include_examples :wiki_page_event, :create
- include_examples :wiki_page_event, :update
- include_examples :wiki_page_event, :delete
-
- describe 'totals' do
- creations = 5
- edits = 3
- deletions = 2
-
- before do
- creations.times do
- described_class.count(:create)
- end
- edits.times do
- described_class.count(:update)
- end
- deletions.times do
- described_class.count(:delete)
- end
- end
-
- it 'can report all totals' do
- expect(described_class.totals).to include(
- wiki_pages_update: edits,
- wiki_pages_create: creations,
- wiki_pages_delete: deletions
- )
- end
- end
-
- describe 'unknown events' do
- error = described_class::UnknownEvent
-
- it 'cannot increment' do
- expect { described_class.count(:wibble) }.to raise_error error
- end
-
- it 'cannot read' do
- expect { described_class.read(:wibble) }.to raise_error error
- end
- end
+describe Gitlab::UsageDataCounters::WikiPageCounter do
+ it_behaves_like 'a redis usage counter', 'Wiki Page', :create
+ it_behaves_like 'a redis usage counter', 'Wiki Page', :update
+ it_behaves_like 'a redis usage counter', 'Wiki Page', :delete
+
+ it_behaves_like 'a redis usage counter with totals', :wiki_pages,
+ create: 5,
+ update: 3,
+ delete: 2
end
diff --git a/spec/lib/prometheus/pid_provider_spec.rb b/spec/lib/prometheus/pid_provider_spec.rb
index e7d500612b1..ba843b27254 100644
--- a/spec/lib/prometheus/pid_provider_spec.rb
+++ b/spec/lib/prometheus/pid_provider_spec.rb
@@ -25,22 +25,60 @@ describe Prometheus::PidProvider do
before do
stub_const('Unicorn::Worker', Class.new)
hide_const('Puma')
+
+ expect(described_class).to receive(:process_name)
+ .at_least(:once)
+ .and_return(process_name)
end
- context 'when `Prometheus::Client::Support::Unicorn` provides worker_id' do
- before do
- expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(1)
+ context 'when unicorn master is specified in process name' do
+ context 'when running in Omnibus' do
+ context 'before the process was renamed' do
+ let(:process_name) { "/opt/gitlab/embedded/bin/unicorn"}
+
+ it { is_expected.to eq 'unicorn_master' }
+ end
+
+ context 'after the process was renamed' do
+ let(:process_name) { "unicorn master -D -E production -c /var/opt/gitlab/gitlab-rails/etc/unicorn.rb /opt/gitlab/embedded/service/gitlab-rails/config.ru" }
+
+ it { is_expected.to eq 'unicorn_master' }
+ end
end
- it { is_expected.to eq 'unicorn_1' }
+ context 'when in development env' do
+ context 'before the process was renamed' do
+ let(:process_name) { "path_to_bindir/bin/unicorn_rails"}
+
+ it { is_expected.to eq 'unicorn_master' }
+ end
+
+ context 'after the process was renamed' do
+ let(:process_name) { "unicorn_rails master -c /gitlab_dir/config/unicorn.rb -E development" }
+
+ it { is_expected.to eq 'unicorn_master' }
+ end
+ end
end
- context 'when no worker_id is provided from `Prometheus::Client::Support::Unicorn`' do
- before do
- expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(nil)
+ context 'when unicorn worker id is specified in process name' do
+ context 'when running in Omnibus' do
+ let(:process_name) { "unicorn worker[1] -D -E production -c /var/opt/gitlab/gitlab-rails/etc/unicorn.rb /opt/gitlab/embedded/service/gitlab-rails/config.ru" }
+
+ it { is_expected.to eq 'unicorn_1' }
end
- it { is_expected.to eq 'unicorn_master' }
+ context 'when in development env' do
+ let(:process_name) { "unicorn_rails worker[1] -c gitlab_dir/config/unicorn.rb -E development" }
+
+ it { is_expected.to eq 'unicorn_1' }
+ end
+ end
+
+ context 'when no specified unicorn master or worker id in process name' do
+ let(:process_name) { "bin/unknown_process"}
+
+ it { is_expected.to eq "process_#{Process.pid}" }
end
end
@@ -48,20 +86,20 @@ describe Prometheus::PidProvider do
before do
stub_const('Puma', Module.new)
hide_const('Unicorn::Worker')
+
+ expect(described_class).to receive(:process_name)
+ .at_least(:once)
+ .and_return(process_name)
end
context 'when cluster worker id is specified in process name' do
- before do
- expect(described_class).to receive(:process_name).and_return('puma: cluster worker 1: 17483 [gitlab-puma-worker]')
- end
+ let(:process_name) { 'puma: cluster worker 1: 17483 [gitlab-puma-worker]' }
it { is_expected.to eq 'puma_1' }
end
context 'when no worker id is specified in process name' do
- before do
- expect(described_class).to receive(:process_name).and_return('bin/puma')
- end
+ let(:process_name) { 'bin/puma' }
it { is_expected.to eq 'puma_master' }
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 56bbcc4c306..dcc4b70a382 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -640,7 +640,7 @@ describe Notify do
project.request_access(user)
project.requesters.find_by(user_id: user.id)
end
- subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) }
+ subject { described_class.member_access_requested_email('project', project_member.id, recipient.id) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
@@ -737,9 +737,9 @@ describe Notify do
describe 'project invitation accepted' do
let(:invited_user) { create(:user, name: 'invited user') }
- let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:recipient) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: maintainer)
+ invitee = invite_to_project(project, inviter: recipient)
invitee.accept_invite!(invited_user)
invitee
end
@@ -747,6 +747,7 @@ describe Notify do
subject { described_class.member_invite_accepted_email('project', project_member.id) }
it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'an email sent to a user'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'appearance header and footer enabled'
@@ -762,16 +763,17 @@ describe Notify do
end
describe 'project invitation declined' do
- let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } }
+ let(:recipient) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:project_member) do
- invitee = invite_to_project(project, inviter: maintainer)
+ invitee = invite_to_project(project, inviter: recipient)
invitee.decline_invite!
invitee
end
- subject { described_class.member_invite_declined_email('Project', project.id, project_member.invite_email, maintainer.id) }
+ subject { described_class.member_invite_declined_email('Project', project.id, project_member.invite_email, recipient.id) }
it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'an email sent to a user'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'appearance header and footer enabled'
@@ -1087,9 +1089,10 @@ describe Notify do
group.request_access(user)
group.requesters.find_by(user_id: user.id)
end
- subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) }
+ subject { described_class.member_access_requested_email('group', group_member.id, recipient.id) }
it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'an email sent to a user'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'appearance header and footer enabled'
@@ -1111,9 +1114,11 @@ describe Notify do
group.request_access(user)
group.requesters.find_by(user_id: user.id)
end
+ let(:recipient) { user }
subject { described_class.member_access_denied_email('group', group.id, user.id) }
it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'an email sent to a user'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'appearance header and footer enabled'
@@ -1128,10 +1133,12 @@ describe Notify do
describe 'group access changed' do
let(:group_member) { create(:group_member, group: group, user: user) }
+ let(:recipient) { user }
subject { described_class.member_access_granted_email('group', group_member.id) }
it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'an email sent to a user'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'appearance header and footer enabled'
diff --git a/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb b/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb
new file mode 100644
index 00000000000..232f6f090c3
--- /dev/null
+++ b/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20190725012225_change_outbound_local_requests_whitelist_default.rb')
+
+describe ChangeOutboundLocalRequestsWhitelistDefault, :migration do
+ let(:application_settings) { table(:application_settings) }
+
+ it 'defaults to empty array' do
+ setting = application_settings.create!
+ setting_with_value = application_settings.create!(outbound_local_requests_whitelist: '{a,b}')
+
+ expect(application_settings.where(outbound_local_requests_whitelist: nil).count).to eq(1)
+
+ migrate!
+
+ expect(application_settings.where(outbound_local_requests_whitelist: nil).count).to eq(0)
+ expect(setting.reload.outbound_local_requests_whitelist).to eq([])
+ expect(setting_with_value.reload.outbound_local_requests_whitelist).to eq(%w[a b])
+ end
+end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e24bbc39761..1fb83fbb088 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1799,7 +1799,7 @@ describe Ci::Pipeline, :mailer do
end
end
- describe '.latest_successful_for' do
+ describe '.latest_successful_for_ref' do
include_context 'with some outdated pipelines'
let!(:latest_successful_pipeline) do
@@ -1807,7 +1807,20 @@ describe Ci::Pipeline, :mailer do
end
it 'returns the latest successful pipeline' do
- expect(described_class.latest_successful_for('ref'))
+ expect(described_class.latest_successful_for_ref('ref'))
+ .to eq(latest_successful_pipeline)
+ end
+ end
+
+ describe '.latest_successful_for_sha' do
+ include_context 'with some outdated pipelines'
+
+ let!(:latest_successful_pipeline) do
+ create_pipeline(:success, 'ref', 'awesomesha', project)
+ end
+
+ it 'returns the latest successful pipeline' do
+ expect(described_class.latest_successful_for_sha('awesomesha'))
.to eq(latest_successful_pipeline)
end
end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 24ea059e871..78b151631c1 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -146,7 +146,7 @@ describe Ci::Runner do
expect(described_class.belonging_to_parent_group_of_project(project.id)).to contain_exactly(runner)
end
- context 'with a parent group with a runner', :nested_groups do
+ context 'with a parent group with a runner' do
let(:runner) { create(:ci_runner, :group, groups: [parent_group]) }
let(:project) { create(:project, group: group) }
let(:group) { create(:group, parent: parent_group) }
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 52661178d76..8f2f1b200e4 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -342,7 +342,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
- context 'when sub-group has configured kubernetes cluster', :nested_groups do
+ context 'when sub-group has configured kubernetes cluster' do
let(:sub_group_cluster) { create(:cluster, :provided_by_gcp, :group) }
let(:sub_group) { sub_group_cluster.group }
let(:project) { create(:project, group: sub_group) }
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index c4f9f62ece5..27f535487c8 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -45,7 +45,7 @@ describe DeploymentPlatform do
is_expected.to eq(group_cluster.platform_kubernetes)
end
- context 'when child group has configured kubernetes cluster', :nested_groups do
+ context 'when child group has configured kubernetes cluster' do
let(:child_group1) { create(:group, parent: group) }
let!(:child_group1_cluster) { create(:cluster_for_group, groups: [child_group1]) }
diff --git a/spec/models/concerns/group_descendant_spec.rb b/spec/models/concerns/group_descendant_spec.rb
index 194caac3fce..192e884f3e8 100644
--- a/spec/models/concerns/group_descendant_spec.rb
+++ b/spec/models/concerns/group_descendant_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe GroupDescendant, :nested_groups do
+describe GroupDescendant do
let(:parent) { create(:group) }
let(:subgroup) { create(:group, parent: parent) }
let(:subsub_group) { create(:group, parent: subgroup) }
@@ -84,7 +84,7 @@ describe GroupDescendant, :nested_groups do
it 'tracks the exception when a parent was not preloaded' do
expect(Gitlab::Sentry).to receive(:track_exception).and_call_original
- expect { GroupDescendant.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError)
+ expect { described_class.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError)
end
it 'recovers if a parent was not reloaded by querying for the parent' do
@@ -93,7 +93,7 @@ describe GroupDescendant, :nested_groups do
# this does not raise in production, so stubbing it here.
allow(Gitlab::Sentry).to receive(:track_exception)
- expect(GroupDescendant.build_hierarchy([subsub_group])).to eq(expected_hierarchy)
+ expect(described_class.build_hierarchy([subsub_group])).to eq(expected_hierarchy)
end
it 'raises an error if not all elements were preloaded' do
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index e19da41c3fe..39680c0e51a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -128,7 +128,7 @@ describe Issuable do
expect(build_issuable(milestone.id).milestone_available?).to be_truthy
end
- it 'returns true with a milestone from the the parent of the issue project group', :nested_groups do
+ it 'returns true with a milestone from the the parent of the issue project group' do
parent = create(:group)
group.update(parent: parent)
milestone = create(:milestone, group: parent)
diff --git a/spec/models/concerns/relative_positioning_spec.rb b/spec/models/concerns/relative_positioning_spec.rb
deleted file mode 100644
index d0ae45f7871..00000000000
--- a/spec/models/concerns/relative_positioning_spec.rb
+++ /dev/null
@@ -1,242 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe RelativePositioning do
- let(:project) { create(:project) }
- let(:issue) { create(:issue, project: project) }
- let(:issue1) { create(:issue, project: project) }
- let(:new_issue) { create(:issue, project: project) }
-
- describe '.move_to_end' do
- it 'moves the object to the end' do
- Issue.move_to_end([issue, issue1])
-
- expect(issue1.prev_relative_position).to eq issue.relative_position
- expect(issue.prev_relative_position).to eq nil
- expect(issue1.next_relative_position).to eq nil
- end
-
- it 'does not perform any moves if all issues have their relative_position set' do
- issue.update!(relative_position: 1)
-
- expect(issue).not_to receive(:save)
-
- Issue.move_to_end([issue])
- end
- end
-
- describe '#max_relative_position' do
- it 'returns maximum position' do
- expect(issue.max_relative_position).to eq issue1.relative_position
- end
- end
-
- describe '#prev_relative_position' do
- it 'returns previous position if there is an issue above' do
- expect(issue1.prev_relative_position).to eq issue.relative_position
- end
-
- it 'returns nil if there is no issue above' do
- expect(issue.prev_relative_position).to eq nil
- end
- end
-
- describe '#next_relative_position' do
- it 'returns next position if there is an issue below' do
- expect(issue.next_relative_position).to eq issue1.relative_position
- end
-
- it 'returns nil if there is no issue below' do
- expect(issue1.next_relative_position).to eq nil
- end
- end
-
- describe '#move_before' do
- it 'moves issue before' do
- [issue1, issue].each(&:move_to_end)
-
- issue.move_before(issue1)
-
- expect(issue.relative_position).to be < issue1.relative_position
- end
- end
-
- describe '#move_after' do
- it 'moves issue after' do
- [issue, issue1].each(&:move_to_end)
-
- issue.move_after(issue1)
-
- expect(issue.relative_position).to be > issue1.relative_position
- end
- end
-
- describe '#move_to_end' do
- before do
- [issue, issue1].each do |issue|
- issue.move_to_end && issue.save
- end
- end
-
- it 'moves issue to the end' do
- new_issue.move_to_end
-
- expect(new_issue.relative_position).to be > issue1.relative_position
- end
- end
-
- describe '#shift_after?' do
- before do
- [issue, issue1].each do |issue|
- issue.move_to_end && issue.save
- end
- end
-
- it 'returns true' do
- issue.update(relative_position: issue1.relative_position - 1)
-
- expect(issue.shift_after?).to be_truthy
- end
-
- it 'returns false' do
- issue.update(relative_position: issue1.relative_position - 2)
-
- expect(issue.shift_after?).to be_falsey
- end
- end
-
- describe '#shift_before?' do
- before do
- [issue, issue1].each do |issue|
- issue.move_to_end && issue.save
- end
- end
-
- it 'returns true' do
- issue.update(relative_position: issue1.relative_position + 1)
-
- expect(issue.shift_before?).to be_truthy
- end
-
- it 'returns false' do
- issue.update(relative_position: issue1.relative_position + 2)
-
- expect(issue.shift_before?).to be_falsey
- end
- end
-
- describe '#move_between' do
- before do
- [issue, issue1].each do |issue|
- issue.move_to_end && issue.save
- end
- end
-
- it 'positions issue between two other' do
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to be > issue.relative_position
- expect(new_issue.relative_position).to be < issue1.relative_position
- end
-
- it 'positions issue between on top' do
- new_issue.move_between(nil, issue)
-
- expect(new_issue.relative_position).to be < issue.relative_position
- end
-
- it 'positions issue between to end' do
- new_issue.move_between(issue1, nil)
-
- expect(new_issue.relative_position).to be > issue1.relative_position
- end
-
- it 'positions issues even when after and before positions are the same' do
- issue1.update relative_position: issue.relative_position
-
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to be > issue.relative_position
- expect(issue.relative_position).to be < issue1.relative_position
- end
-
- it 'positions issues between other two if distance is 1' do
- issue1.update relative_position: issue.relative_position + 1
-
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to be > issue.relative_position
- expect(issue.relative_position).to be < issue1.relative_position
- end
-
- it 'positions issue in the middle of other two if distance is big enough' do
- issue.update relative_position: 6000
- issue1.update relative_position: 10000
-
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to eq(8000)
- end
-
- it 'positions issue closer to the middle if we are at the very top' do
- issue1.update relative_position: 6000
-
- new_issue.move_between(nil, issue1)
-
- expect(new_issue.relative_position).to eq(6000 - RelativePositioning::IDEAL_DISTANCE)
- end
-
- it 'positions issue closer to the middle if we are at the very bottom' do
- issue.update relative_position: 6000
- issue1.update relative_position: nil
-
- new_issue.move_between(issue, nil)
-
- expect(new_issue.relative_position).to eq(6000 + RelativePositioning::IDEAL_DISTANCE)
- end
-
- it 'positions issue in the middle of other two if distance is not big enough' do
- issue.update relative_position: 100
- issue1.update relative_position: 400
-
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to eq(250)
- end
-
- it 'positions issue in the middle of other two is there is no place' do
- issue.update relative_position: 100
- issue1.update relative_position: 101
-
- new_issue.move_between(issue, issue1)
-
- expect(new_issue.relative_position).to be_between(issue.relative_position, issue1.relative_position)
- end
-
- it 'uses rebalancing if there is no place' do
- issue.update relative_position: 100
- issue1.update relative_position: 101
- issue2 = create(:issue, relative_position: 102, project: project)
- new_issue.update relative_position: 103
-
- new_issue.move_between(issue1, issue2)
- new_issue.save!
-
- expect(new_issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
- expect(issue.reload.relative_position).not_to eq(100)
- end
-
- it 'positions issue right if we pass none-sequential parameters' do
- issue.update relative_position: 99
- issue1.update relative_position: 101
- issue2 = create(:issue, relative_position: 102, project: project)
- new_issue.update relative_position: 103
-
- new_issue.move_between(issue, issue2)
- new_issue.save!
-
- expect(new_issue.relative_position).to be(100)
- end
- end
-end
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index 013112d1d51..935838ce294 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -16,7 +16,7 @@ describe ContainerRepository do
host_port: 'registry.gitlab')
stub_request(:get, 'http://registry.gitlab/v2/group/test/my_image/tags/list')
- .with(headers: { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' })
+ .with(headers: { 'Accept' => ContainerRegistry::Client::ACCEPTED_TYPES.join(', ') })
.to_return(
status: 200,
body: JSON.dump(tags: ['test_tag']),
diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb
index 0aadb1f3a5e..7c574a8b6c8 100644
--- a/spec/models/deployment_metrics_spec.rb
+++ b/spec/models/deployment_metrics_spec.rb
@@ -49,18 +49,6 @@ describe DeploymentMetrics do
it { is_expected.to be_truthy }
end
-
- context 'fallback deployment platform' do
- let(:cluster) { create(:cluster, :provided_by_user, environment_scope: '*', projects: [deployment.project]) }
- let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
-
- before do
- expect(deployment.project).to receive(:deployment_platform).and_return(cluster.platform)
- expect(cluster.application_prometheus).to receive(:can_query?).and_return(true)
- end
-
- it { is_expected.to be_truthy }
- end
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 90e0900445e..7e9bbf5a407 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -71,7 +71,7 @@ describe Group do
end
end
- describe '#notification_settings', :nested_groups do
+ describe '#notification_settings' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:sub_group) { create(:group, parent_id: group.id) }
@@ -237,7 +237,7 @@ describe Group do
it { is_expected.to match_array([private_group, internal_group, group]) }
end
- context 'when user is a member of private subgroup', :postgresql do
+ context 'when user is a member of private subgroup' do
let!(:private_subgroup) { create(:group, :private, parent: private_group) }
before do
@@ -416,7 +416,7 @@ describe Group do
it { expect(group.last_owner?(@members[:owner])).to be_falsy }
end
- context 'with owners from a parent', :postgresql do
+ context 'with owners from a parent' do
before do
parent_group = create(:group)
create(:group_member, :owner, group: parent_group)
@@ -524,7 +524,7 @@ describe Group do
it { expect(subject.parent).to be_kind_of(described_class) }
end
- describe '#members_with_parents', :nested_groups do
+ describe '#members_with_parents' do
let!(:group) { create(:group, :nested) }
let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
@@ -535,7 +535,7 @@ describe Group do
end
end
- describe '#direct_and_indirect_members', :nested_groups do
+ describe '#direct_and_indirect_members' do
let!(:group) { create(:group, :nested) }
let!(:sub_group) { create(:group, parent: group) }
let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) }
@@ -552,7 +552,7 @@ describe Group do
end
end
- describe '#users_with_descendants', :nested_groups do
+ describe '#users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
@@ -571,7 +571,7 @@ describe Group do
end
end
- describe '#direct_and_indirect_users', :nested_groups do
+ describe '#direct_and_indirect_users' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
let(:user_c) { create(:user) }
@@ -601,7 +601,7 @@ describe Group do
end
end
- describe '#project_users_with_descendants', :nested_groups do
+ describe '#project_users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
let(:user_c) { create(:user) }
@@ -678,7 +678,7 @@ describe Group do
end
end
- context 'sub groups and projects', :nested_groups do
+ context 'sub groups and projects' do
it 'enables two_factor_requirement for group member' do
group.add_user(user, GroupMember::OWNER)
@@ -687,7 +687,7 @@ describe Group do
expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
end
- context 'expanded group members', :nested_groups do
+ context 'expanded group members' do
let(:indirect_user) { create(:user) }
it 'enables two_factor_requirement for subgroup member' do
@@ -720,7 +720,7 @@ describe Group do
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
- it 'does not enable two_factor_requirement for subgroup child project member', :nested_groups do
+ it 'does not enable two_factor_requirement for subgroup child project member' do
subgroup = create(:group, :nested, parent: group)
project = create(:project, group: subgroup)
project.add_maintainer(user)
@@ -820,7 +820,7 @@ describe Group do
it_behaves_like 'ref is protected'
end
- context 'when group has children', :postgresql do
+ context 'when group has children' do
let(:group_child) { create(:group, parent: group) }
let(:group_child_2) { create(:group, parent: group_child) }
let(:group_child_3) { create(:group, parent: group_child_2) }
@@ -843,7 +843,7 @@ describe Group do
end
end
- describe '#highest_group_member', :nested_groups do
+ describe '#highest_group_member' do
let(:nested_group) { create(:group, parent: group) }
let(:nested_group_2) { create(:group, parent: nested_group) }
let(:user) { create(:user) }
@@ -932,7 +932,7 @@ describe Group do
it { is_expected.to eq(config) }
end
- context 'with parent groups', :nested_groups do
+ context 'with parent groups' do
where(:instance_value, :parent_value, :group_value, :config) do
# Instance level enabled
true | nil | nil | { status: true, scope: :instance }
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index d5b016dc8f6..2e7d78d77a8 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -871,4 +871,12 @@ describe Issue do
expect(issue.labels_hook_attrs).to eq([label.hook_attrs])
end
end
+
+ context "relative positioning" do
+ it_behaves_like "a class that supports relative positioning" do
+ let(:project) { create(:project) }
+ let(:factory) { :issue }
+ let(:default_params) { { project: project } }
+ end
+ end
end
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index f227abd3dae..ebb0bfca369 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -69,7 +69,7 @@ describe GroupMember do
end
end
- context 'access levels', :nested_groups do
+ context 'access levels' do
context 'with parent group' do
it_behaves_like 'inherited access level as a member of entity' do
let(:entity) { create(:group, parent: parent_entity) }
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 497764b6825..79c39b81196 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -130,7 +130,7 @@ describe ProjectMember do
end
end
- context 'with parent group and a subgroup', :nested_groups do
+ context 'with parent group and a subgroup' do
it_behaves_like 'inherited access level as a member of entity' do
let(:subgroup) { create(:group, parent: parent_entity) }
let(:entity) { create(:project, group: subgroup) }
diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb
index 3229a32234e..5341278db7c 100644
--- a/spec/models/namespace/root_storage_statistics_spec.rb
+++ b/spec/models/namespace/root_storage_statistics_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
it_behaves_like 'data refresh'
- context 'with subgroups', :nested_groups do
+ context 'with subgroups' do
let(:subgroup1) { create(:group, parent: namespace)}
let(:subgroup2) { create(:group, parent: subgroup1)}
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index f908f3504e0..2b9c3c43af9 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -191,7 +191,7 @@ describe Namespace do
end
end
- describe '#ancestors_upto', :nested_groups do
+ describe '#ancestors_upto' do
let(:parent) { create(:group) }
let(:child) { create(:group, parent: parent) }
let(:child2) { create(:group, parent: child) }
@@ -271,7 +271,7 @@ describe Namespace do
end
end
- context 'with subgroups', :nested_groups do
+ context 'with subgroups' do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
@@ -475,7 +475,7 @@ describe Namespace do
end
end
- describe '#self_and_hierarchy', :nested_groups do
+ describe '#self_and_hierarchy' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -490,7 +490,7 @@ describe Namespace do
end
end
- describe '#ancestors', :nested_groups do
+ describe '#ancestors' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -504,7 +504,7 @@ describe Namespace do
end
end
- describe '#self_and_ancestors', :nested_groups do
+ describe '#self_and_ancestors' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -518,7 +518,7 @@ describe Namespace do
end
end
- describe '#descendants', :nested_groups do
+ describe '#descendants' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -534,7 +534,7 @@ describe Namespace do
end
end
- describe '#self_and_descendants', :nested_groups do
+ describe '#self_and_descendants' do
let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
@@ -550,7 +550,7 @@ describe Namespace do
end
end
- describe '#users_with_descendants', :nested_groups do
+ describe '#users_with_descendants' do
let(:user_a) { create(:user) }
let(:user_b) { create(:user) }
@@ -597,7 +597,7 @@ describe Namespace do
it { expect(group.all_pipelines.to_a).to match_array([pipeline1, pipeline2]) }
end
- describe '#share_with_group_lock with subgroups', :nested_groups do
+ describe '#share_with_group_lock with subgroups' do
context 'when creating a subgroup' do
let(:subgroup) { create(:group, parent: root_group )}
@@ -738,7 +738,7 @@ describe Namespace do
end
describe '#root_ancestor' do
- it 'returns the top most ancestor', :nested_groups do
+ it 'returns the top most ancestor' do
root_group = create(:group)
nested_group = create(:group, parent: root_group)
deep_nested_group = create(:group, parent: nested_group)
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index 20278d81f6d..4122736c148 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -49,7 +49,7 @@ describe NotificationRecipient do
end
context '#notification_setting' do
- context 'for child groups', :nested_groups do
+ context 'for child groups' do
let!(:moved_group) { create(:group) }
let(:group) { create(:group) }
let(:sub_group_1) { create(:group, parent: group) }
diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb
index 95ae204a8a8..d435fccc09a 100644
--- a/spec/models/postgresql/replication_slot_spec.rb
+++ b/spec/models/postgresql/replication_slot_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Postgresql::ReplicationSlot, :postgresql do
+describe Postgresql::ReplicationSlot do
describe '.in_use?' do
it 'returns true when replication slots are present' do
expect(described_class).to receive(:exists?).and_return(true)
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index dad5506900b..cd997224122 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -25,7 +25,7 @@ describe ProjectGroupLink do
expect(project_group_link).not_to be_valid
end
- it "doesn't allow a project to be shared with an ancestor of the group it is in", :nested_groups do
+ it "doesn't allow a project to be shared with an ancestor of the group it is in" do
project_group_link.group = parent_group
expect(project_group_link).not_to be_valid
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 7d458324c20..24dfd0c5605 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2019,62 +2019,33 @@ describe Project do
end
end
- describe '#latest_successful_build_for' do
+ describe '#latest_successful_build_for_ref' do
let(:project) { create(:project, :repository) }
let(:pipeline) { create_pipeline(project) }
- context 'with many builds' do
- it 'gives the latest builds from latest pipeline' do
- pipeline1 = create_pipeline(project)
- pipeline2 = create_pipeline(project)
- create_build(pipeline1, 'test')
- create_build(pipeline1, 'test2')
- build1_p2 = create_build(pipeline2, 'test')
- create_build(pipeline2, 'test2')
-
- expect(project.latest_successful_build_for(build1_p2.name))
- .to eq(build1_p2)
- end
- end
+ it_behaves_like 'latest successful build for sha or ref'
- context 'with succeeded pipeline' do
- let!(:build) { create_build }
+ subject { project.latest_successful_build_for_ref(build_name) }
- context 'standalone pipeline' do
- it 'returns builds for ref for default_branch' do
- expect(project.latest_successful_build_for(build.name))
- .to eq(build)
- end
+ context 'with a specified ref' do
+ let(:build) { create_build }
- it 'returns empty relation if the build cannot be found' do
- expect(project.latest_successful_build_for('TAIL'))
- .to be_nil
- end
- end
-
- context 'with some pending pipeline' do
- before do
- create_build(create_pipeline(project, 'pending'))
- end
+ subject { project.latest_successful_build_for_ref(build.name, project.default_branch) }
- it 'gives the latest build from latest pipeline' do
- expect(project.latest_successful_build_for(build.name))
- .to eq(build)
- end
- end
+ it { is_expected.to eq(build) }
end
+ end
- context 'with pending pipeline' do
- it 'returns empty relation' do
- pipeline.update(status: 'pending')
- pending_build = create_build(pipeline)
+ describe '#latest_successful_build_for_sha' do
+ let(:project) { create(:project, :repository) }
+ let(:pipeline) { create_pipeline(project) }
- expect(project.latest_successful_build_for(pending_build.name)).to be_nil
- end
- end
+ it_behaves_like 'latest successful build for sha or ref'
+
+ subject { project.latest_successful_build_for_sha(build_name, project.commit.sha) }
end
- describe '#latest_successful_build_for!' do
+ describe '#latest_successful_build_for_ref!' do
let(:project) { create(:project, :repository) }
let(:pipeline) { create_pipeline(project) }
@@ -2087,7 +2058,7 @@ describe Project do
build1_p2 = create_build(pipeline2, 'test')
create_build(pipeline2, 'test2')
- expect(project.latest_successful_build_for(build1_p2.name))
+ expect(project.latest_successful_build_for_ref!(build1_p2.name))
.to eq(build1_p2)
end
end
@@ -2097,12 +2068,12 @@ describe Project do
context 'standalone pipeline' do
it 'returns builds for ref for default_branch' do
- expect(project.latest_successful_build_for!(build.name))
+ expect(project.latest_successful_build_for_ref!(build.name))
.to eq(build)
end
it 'returns exception if the build cannot be found' do
- expect { project.latest_successful_build_for!(build.name, 'TAIL') }
+ expect { project.latest_successful_build_for_ref!(build.name, 'TAIL') }
.to raise_error(ActiveRecord::RecordNotFound)
end
end
@@ -2113,7 +2084,7 @@ describe Project do
end
it 'gives the latest build from latest pipeline' do
- expect(project.latest_successful_build_for!(build.name))
+ expect(project.latest_successful_build_for_ref!(build.name))
.to eq(build)
end
end
@@ -2124,7 +2095,7 @@ describe Project do
pipeline.update(status: 'pending')
pending_build = create_build(pipeline)
- expect { project.latest_successful_build_for!(pending_build.name) }
+ expect { project.latest_successful_build_for_ref!(pending_build.name) }
.to raise_error(ActiveRecord::RecordNotFound)
end
end
@@ -2292,7 +2263,7 @@ describe Project do
end
end
- describe '#ancestors_upto', :nested_groups do
+ describe '#ancestors_upto' do
let(:parent) { create(:group) }
let(:child) { create(:group, parent: parent) }
let(:child2) { create(:group, parent: child) }
@@ -2331,7 +2302,7 @@ describe Project do
it { is_expected.to eq(group) }
end
- context 'in a nested group', :nested_groups do
+ context 'in a nested group' do
let(:root) { create(:group) }
let(:child) { create(:group, parent: root) }
let(:project) { create(:project, group: child) }
@@ -2479,7 +2450,7 @@ describe Project do
expect(forked_project.in_fork_network_of?(project)).to be_truthy
end
- it 'is true for a fork of a fork', :postgresql do
+ it 'is true for a fork of a fork' do
other_fork = fork_project(forked_project)
expect(other_fork.in_fork_network_of?(project)).to be_truthy
@@ -3801,7 +3772,7 @@ describe Project do
end
end
- context 'when enabled on root parent', :nested_groups do
+ context 'when enabled on root parent' do
let(:parent_group) { create(:group, parent: create(:group, :auto_devops_enabled)) }
context 'when auto devops instance enabled' do
@@ -3821,7 +3792,7 @@ describe Project do
end
end
- context 'when disabled on root parent', :nested_groups do
+ context 'when disabled on root parent' do
let(:parent_group) { create(:group, parent: create(:group, :auto_devops_disabled)) }
context 'when auto devops instance enabled' do
@@ -4033,7 +4004,7 @@ describe Project do
context 'with a ref that is not the default branch' do
it 'returns the latest successful pipeline for the given ref' do
- expect(project.ci_pipelines).to receive(:latest_successful_for).with('foo')
+ expect(project.ci_pipelines).to receive(:latest_successful_for_ref).with('foo')
project.latest_successful_pipeline_for('foo')
end
@@ -4061,7 +4032,7 @@ describe Project do
it 'memoizes and returns the latest successful pipeline for the default branch' do
pipeline = double(:pipeline)
- expect(project.ci_pipelines).to receive(:latest_successful_for)
+ expect(project.ci_pipelines).to receive(:latest_successful_for_ref)
.with(project.default_branch)
.and_return(pipeline)
.once
@@ -4264,18 +4235,16 @@ describe Project do
expect(project.badges.count).to eq 3
end
- if Group.supports_nested_objects?
- context 'with nested_groups' do
- let(:parent_group) { create(:group) }
+ context 'with nested_groups' do
+ let(:parent_group) { create(:group) }
- before do
- create_list(:group_badge, 2, group: project_group)
- project_group.update(parent: parent_group)
- end
+ before do
+ create_list(:group_badge, 2, group: project_group)
+ project_group.update(parent: parent_group)
+ end
- it 'returns the project and the project nested groups badges' do
- expect(project.badges.count).to eq 5
- end
+ it 'returns the project and the project nested groups badges' do
+ expect(project.badges.count).to eq 5
end
end
end
diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb
index b5bf294790a..9aeef7c3b4b 100644
--- a/spec/models/todo_spec.rb
+++ b/spec/models/todo_spec.rb
@@ -262,11 +262,7 @@ describe Todo do
todo2 = create(:todo, group: child_group)
todos = described_class.for_group_and_descendants(parent_group)
- expect(todos).to include(todo1)
-
- # Nested groups only work on PostgreSQL, so on MySQL todo2 won't be
- # present.
- expect(todos).to include(todo2) if Gitlab::Database.postgresql?
+ expect(todos).to contain_exactly(todo1, todo2)
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 2d20f8c78cc..35c335c5b5c 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -985,7 +985,7 @@ describe User do
it { expect(user.namespaces).to contain_exactly(user.namespace, group) }
it { expect(user.manageable_namespaces).to contain_exactly(user.namespace, group) }
- context 'with child groups', :nested_groups do
+ context 'with child groups' do
let!(:subgroup) { create(:group, parent: group) }
describe '#manageable_namespaces' do
@@ -2082,11 +2082,7 @@ describe User do
subject { user.membership_groups }
- if Group.supports_nested_objects?
- it { is_expected.to contain_exactly parent_group, child_group }
- else
- it { is_expected.to contain_exactly parent_group }
- end
+ it { is_expected.to contain_exactly parent_group, child_group }
end
describe '#authorizations_for_projects' do
@@ -2386,7 +2382,7 @@ describe User do
it_behaves_like :member
end
- context 'with subgroup with different owner for project runner', :nested_groups do
+ context 'with subgroup with different owner for project runner' do
let(:group) { create(:group) }
let(:another_user) { create(:user) }
let(:subgroup) { create(:group, parent: group) }
@@ -2490,22 +2486,16 @@ describe User do
group.add_owner(user)
end
- if Group.supports_nested_objects?
- it 'returns all groups' do
- is_expected.to match_array [
- group,
- nested_group_1, nested_group_1_1,
- nested_group_2, nested_group_2_1
- ]
- end
- else
- it 'returns the top-level groups' do
- is_expected.to match_array [group]
- end
+ it 'returns all groups' do
+ is_expected.to match_array [
+ group,
+ nested_group_1, nested_group_1_1,
+ nested_group_2, nested_group_2_1
+ ]
end
end
- context 'user is member of the first child (internal node), branch 1', :nested_groups do
+ context 'user is member of the first child (internal node), branch 1' do
before do
nested_group_1.add_owner(user)
end
@@ -2518,7 +2508,7 @@ describe User do
end
end
- context 'user is member of the first child (internal node), branch 2', :nested_groups do
+ context 'user is member of the first child (internal node), branch 2' do
before do
nested_group_2.add_owner(user)
end
@@ -2531,7 +2521,7 @@ describe User do
end
end
- context 'user is member of the last child (leaf node)', :nested_groups do
+ context 'user is member of the last child (leaf node)' do
before do
nested_group_1_1.add_owner(user)
end
@@ -2687,7 +2677,7 @@ describe User do
end
end
- context 'with 2FA requirement from expanded groups', :nested_groups do
+ context 'with 2FA requirement from expanded groups' do
let!(:group1) { create :group, require_two_factor_authentication: true }
let!(:group1a) { create :group, parent: group1 }
@@ -2702,7 +2692,7 @@ describe User do
end
end
- context 'with 2FA requirement on nested child group', :nested_groups do
+ context 'with 2FA requirement on nested child group' do
let!(:group1) { create :group, require_two_factor_authentication: false }
let!(:group1a) { create :group, require_two_factor_authentication: true, parent: group1 }
diff --git a/spec/policies/group_member_policy_spec.rb b/spec/policies/group_member_policy_spec.rb
index 7bd7184cffe..a4f3301a064 100644
--- a/spec/policies/group_member_policy_spec.rb
+++ b/spec/policies/group_member_policy_spec.rb
@@ -58,7 +58,7 @@ describe GroupMemberPolicy do
end
end
- context 'with the group parent', :postgresql do
+ context 'with the group parent' do
let(:current_user) { create :user }
let(:subgroup) { create(:group, :private, parent: group)}
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index dc3675a7b9e..be55d94daec 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -51,7 +51,7 @@ describe GroupPolicy do
it { expect_allowed(:read_label, :read_list) }
- context 'in subgroups', :nested_groups do
+ context 'in subgroups' do
let(:subgroup) { create(:group, :private, parent: group) }
let(:project) { create(:project, namespace: subgroup) }
@@ -104,8 +104,6 @@ describe GroupPolicy do
end
it 'allows every maintainer permission plus creating subgroups' do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
-
create_subgroup_permission = [:create_subgroup]
updated_maintainer_permissions =
maintainer_permissions + create_subgroup_permission
@@ -122,8 +120,6 @@ describe GroupPolicy do
context 'with subgroup_creation_level set to owner' do
it 'allows every maintainer permission' do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
-
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
@@ -137,8 +133,6 @@ describe GroupPolicy do
let(:current_user) { owner }
it do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
-
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
@@ -151,8 +145,6 @@ describe GroupPolicy do
let(:current_user) { admin }
it do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
-
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
@@ -161,52 +153,7 @@ describe GroupPolicy do
end
end
- describe 'when nested group support feature is disabled' do
- before do
- allow(Group).to receive(:supports_nested_objects?).and_return(false)
- end
-
- context 'admin' do
- let(:current_user) { admin }
-
- it 'allows every owner permission except creating subgroups' do
- create_subgroup_permission = [:create_subgroup]
- updated_owner_permissions =
- owner_permissions - create_subgroup_permission
-
- expect_disallowed(*create_subgroup_permission)
- expect_allowed(*updated_owner_permissions)
- end
- end
-
- context 'owner' do
- let(:current_user) { owner }
-
- it 'allows every owner permission except creating subgroups' do
- create_subgroup_permission = [:create_subgroup]
- updated_owner_permissions =
- owner_permissions - create_subgroup_permission
-
- expect_disallowed(*create_subgroup_permission)
- expect_allowed(*updated_owner_permissions)
- end
- end
-
- context 'maintainer' do
- let(:current_user) { maintainer }
-
- it 'allows every maintainer permission except creating subgroups' do
- create_subgroup_permission = [:create_subgroup]
- updated_maintainer_permissions =
- maintainer_permissions - create_subgroup_permission
-
- expect_disallowed(*create_subgroup_permission)
- expect_allowed(*updated_maintainer_permissions)
- end
- end
- end
-
- describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do
+ describe 'private nested group use the highest access level from the group and inherited permissions' do
let(:nested_group) do
create(:group, :private, :owner_subgroup_creation_only, parent: group)
end
@@ -289,8 +236,6 @@ describe GroupPolicy do
let(:current_user) { owner }
it do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
-
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions)
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index 7054a70e2ed..6b988e2645b 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -43,7 +43,7 @@ describe Clusters::ClusterPresenter do
end
shared_examples 'ancestor clusters' do
- context 'ancestor clusters', :nested_groups do
+ context 'ancestor clusters' do
let(:root_group) { create(:group, name: 'Root Group') }
let(:parent) { create(:group, name: 'parent', parent: root_group) }
let(:child) { create(:group, name: 'child', parent: parent) }
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index de79e8c4c5c..0b9c0c2ebe9 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -63,7 +63,7 @@ describe API::Boards do
end
end
- describe "POST /groups/:id/boards/lists", :nested_groups do
+ describe "POST /groups/:id/boards/lists" do
set(:group) { create(:group) }
set(:board_parent) { create(:group, parent: group ) }
let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" }
diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb
index 63fa16c79ca..815e9531ecf 100644
--- a/spec/requests/api/graphql/namespace/projects_spec.rb
+++ b/spec/requests/api/graphql/namespace/projects_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'getting projects', :nested_groups do
+describe 'getting projects' do
include GraphqlHelpers
let(:group) { create(:group) }
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index 3769f8b78e4..fcea57d9df7 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -94,7 +94,7 @@ describe API::GroupLabels do
expect(response).to have_gitlab_http_status(400)
end
- it "does not delete parent's group labels", :nested_groups do
+ it "does not delete parent's group labels" do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
@@ -127,7 +127,7 @@ describe API::GroupLabels do
expect(json_response['description']).to eq('test')
end
- it "does not update parent's group label", :nested_groups do
+ it "does not update parent's group label" do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 52d926d5484..50f36141aed 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -530,7 +530,7 @@ describe API::Groups do
expect(json_response.length).to eq(2)
end
- it "returns projects including those in subgroups", :nested_groups do
+ it "returns projects including those in subgroups" do
subgroup = create(:group, parent: group1)
create(:project, group: subgroup)
create(:project, group: subgroup)
@@ -642,7 +642,7 @@ describe API::Groups do
end
end
- describe 'GET /groups/:id/subgroups', :nested_groups do
+ describe 'GET /groups/:id/subgroups' do
let!(:subgroup1) { create(:group, parent: group1) }
let!(:subgroup2) { create(:group, :private, parent: group1) }
let!(:subgroup3) { create(:group, :private, parent: group2) }
@@ -786,7 +786,7 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(403)
end
- context 'as owner', :nested_groups do
+ context 'as owner' do
before do
group2.add_owner(user1)
end
@@ -798,7 +798,7 @@ describe API::Groups do
end
end
- context 'as maintainer', :nested_groups do
+ context 'as maintainer' do
before do
group2.add_maintainer(user1)
end
@@ -825,7 +825,7 @@ describe API::Groups do
expect(json_response["visibility"]).to eq(Gitlab::VisibilityLevel.string_level(Gitlab::CurrentSettings.current_application_settings.default_group_visibility))
end
- it "creates a nested group", :nested_groups do
+ it "creates a nested group" do
parent = create(:group)
parent.add_owner(user3)
group = attributes_for(:group, { parent_id: parent.id })
diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb
index 9a41d790945..5916bb11516 100644
--- a/spec/requests/api/issues/get_group_issues_spec.rb
+++ b/spec/requests/api/issues/get_group_issues_spec.rb
@@ -82,7 +82,7 @@ describe API::Issues do
end
end
- context 'when group has subgroups', :nested_groups do
+ context 'when group has subgroups' do
let(:subgroup_1) { create(:group, parent: group) }
let(:subgroup_2) { create(:group, parent: subgroup_1) }
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 55f38079b1f..26f6e705528 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -99,7 +99,7 @@ describe API::Members do
end
end
- describe 'GET /:source_type/:id/members/all', :nested_groups do
+ describe 'GET /:source_type/:id/members/all' do
let(:nested_user) { create(:user) }
let(:project_user) { create(:user) }
let(:linked_group_user) { create(:user) }
@@ -238,7 +238,7 @@ describe API::Members do
end
context 'access levels' do
- it 'does not create the member if group level is higher', :nested_groups do
+ it 'does not create the member if group level is higher' do
parent = create(:group)
group.update(parent: parent)
@@ -252,7 +252,7 @@ describe API::Members do
expect(json_response['message']['access_level']).to eq(["should be greater than or equal to Developer inherited membership from group #{parent.name}"])
end
- it 'creates the member if group level is lower', :nested_groups do
+ it 'creates the member if group level is lower' do
parent = create(:group)
group.update(parent: parent)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index ced853caab4..7a6f1cd548c 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -723,7 +723,7 @@ describe API::MergeRequests do
it_behaves_like 'merge requests list'
- context 'when have subgroups', :nested_groups do
+ context 'when have subgroups' do
let!(:group) { create(:group, :public) }
let!(:subgroup) { create(:group, parent: group) }
let!(:project) { create(:project, :public, :repository, creator: user, namespace: subgroup, only_allow_merge_if_pipeline_succeeds: false) }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index fee300e9d7a..5b3a2412aff 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1357,7 +1357,7 @@ describe API::Projects do
end
end
- context 'nested group project', :nested_groups do
+ context 'nested group project' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:project2) { create(:project, group: nested_group) }
diff --git a/spec/requests/api/resource_label_events_spec.rb b/spec/requests/api/resource_label_events_spec.rb
index 37b46eaeb86..25bea627b0c 100644
--- a/spec/requests/api/resource_label_events_spec.rb
+++ b/spec/requests/api/resource_label_events_spec.rb
@@ -4,55 +4,12 @@ require 'spec_helper'
describe API::ResourceLabelEvents do
set(:user) { create(:user) }
- set(:project) { create(:project, :public, :repository, namespace: user.namespace) }
- set(:private_user) { create(:user) }
+ set(:project) { create(:project, :public, namespace: user.namespace) }
before do
project.add_developer(user)
end
- shared_examples 'resource_label_events API' do |parent_type, eventable_type, id_name|
- describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events" do
- it "returns an array of resource label events" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.first['id']).to eq(event.id)
- end
-
- it "returns a 404 error when eventable id not found" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/12345/resource_label_events", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it "returns 404 when not authorized" do
- parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
-
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", private_user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events/:event_id" do
- it "returns a resource label event by id" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/#{event.id}", user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['id']).to eq(event.id)
- end
-
- it "returns a 404 error if resource label event not found" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/12345", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
- end
-
context 'when eventable is an Issue' do
let(:issue) { create(:issue, project: project, author: user) }
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 86e41cbdf00..025568d8bea 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -104,7 +104,7 @@ describe 'OpenID Connect requests' do
expect(json_response).to match(id_token_claims.merge(user_info_claims))
expected_groups = [group1.full_path, group3.full_path]
- expected_groups << group4.full_path if Group.supports_nested_objects?
+ expected_groups << group4.full_path
expect(json_response['groups']).to match_array(expected_groups)
end
diff --git a/spec/serializers/group_child_entity_spec.rb b/spec/serializers/group_child_entity_spec.rb
index b58d95ccb43..00e2f931549 100644
--- a/spec/serializers/group_child_entity_spec.rb
+++ b/spec/serializers/group_child_entity_spec.rb
@@ -62,7 +62,7 @@ describe GroupChildEntity do
it_behaves_like 'group child json'
end
- describe 'for a group', :nested_groups do
+ describe 'for a group' do
let(:description) { 'Awesomeness' }
let(:object) do
create(:group, :nested, :with_avatar,
diff --git a/spec/serializers/group_child_serializer_spec.rb b/spec/serializers/group_child_serializer_spec.rb
index 5541ada3750..c9e8535585b 100644
--- a/spec/serializers/group_child_serializer_spec.rb
+++ b/spec/serializers/group_child_serializer_spec.rb
@@ -16,7 +16,7 @@ describe GroupChildSerializer do
end
end
- context 'with a hierarchy', :nested_groups do
+ context 'with a hierarchy' do
let(:parent) { create(:group) }
subject(:serializer) do
@@ -75,7 +75,7 @@ describe GroupChildSerializer do
expect(serializer.represent(build_list(:project, 2))).to be_kind_of(Array)
end
- context 'with a hierarchy', :nested_groups do
+ context 'with a hierarchy' do
let(:parent) { create(:group) }
subject(:serializer) do
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 54e6abc2d3a..7f9827329b3 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -126,7 +126,7 @@ describe PipelineSerializer do
expect(subject.all? { |entry| entry[:merge_request].present? }).to be_truthy
end
- it 'preloads related merge requests', :postgresql do
+ it 'preloads related merge requests' do
recorded = ActiveRecord::QueryRecorder.new { subject }
expect(recorded.log)
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 40878e24cb4..931b67b2950 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -112,7 +112,7 @@ describe Boards::Issues::ListService do
it_behaves_like 'issues list service'
end
- context 'and group is an ancestor', :nested_groups do
+ context 'and group is an ancestor' do
let(:parent) { create(:group) }
let(:group) { create(:group, parent: parent) }
let!(:backlog) { create(:backlog_list, board: board) }
diff --git a/spec/services/groups/auto_devops_service_spec.rb b/spec/services/groups/auto_devops_service_spec.rb
index 7f8ab517cef..7591b2f6f12 100644
--- a/spec/services/groups/auto_devops_service_spec.rb
+++ b/spec/services/groups/auto_devops_service_spec.rb
@@ -47,7 +47,7 @@ describe Groups::AutoDevopsService, '#execute' do
expect(subgroup_1.auto_devops_enabled?).to eq(false)
end
- context 'when subgroups have projects', :nested_groups do
+ context 'when subgroups have projects' do
it 'reflects changes on projects' do
subgroup_1 = create(:group, parent: group)
project_1 = create(:project, namespace: subgroup_1)
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index a7c95428485..0f9f20de586 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -44,7 +44,7 @@ describe Groups::CreateService, '#execute' do
end
end
- describe 'creating subgroup', :nested_groups do
+ describe 'creating subgroup' do
let!(:group) { create(:group) }
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
@@ -54,47 +54,31 @@ describe Groups::CreateService, '#execute' do
end
it { is_expected.to be_persisted }
+ end
- context 'when nested groups feature is disabled' do
- it 'does not save group and returns an error' do
- allow(Group).to receive(:supports_nested_objects?).and_return(false)
+ context 'as guest' do
+ it 'does not save group and returns an error' do
+ is_expected.not_to be_persisted
- is_expected.not_to be_persisted
- expect(subject.errors[:parent_id]).to include('You don’t have permission to create a subgroup in this group.')
- expect(subject.parent_id).to be_nil
- end
+ expect(subject.errors[:parent_id].first).to eq('You don’t have permission to create a subgroup in this group.')
+ expect(subject.parent_id).to be_nil
end
end
- context 'when nested groups feature is enabled' do
+ context 'as owner' do
before do
- allow(Group).to receive(:supports_nested_objects?).and_return(true)
- end
-
- context 'as guest' do
- it 'does not save group and returns an error' do
- is_expected.not_to be_persisted
-
- expect(subject.errors[:parent_id].first).to eq('You don’t have permission to create a subgroup in this group.')
- expect(subject.parent_id).to be_nil
- end
+ group.add_owner(user)
end
- context 'as owner' do
- before do
- group.add_owner(user)
- end
+ it { is_expected.to be_persisted }
+ end
- it { is_expected.to be_persisted }
+ context 'as maintainer' do
+ before do
+ group.add_maintainer(user)
end
- context 'as maintainer' do
- before do
- group.add_maintainer(user)
- end
-
- it { is_expected.to be_persisted }
- end
+ it { is_expected.to be_persisted }
end
end
diff --git a/spec/services/groups/nested_create_service_spec.rb b/spec/services/groups/nested_create_service_spec.rb
index 13acf9e055b..b30392c1b12 100644
--- a/spec/services/groups/nested_create_service_spec.rb
+++ b/spec/services/groups/nested_create_service_spec.rb
@@ -28,35 +28,7 @@ describe Groups::NestedCreateService do
end
end
- describe 'without subgroups' do
- let(:params) { { group_path: 'a-group' } }
-
- before do
- allow(Group).to receive(:supports_nested_objects?) { false }
- end
-
- it 'creates the group' do
- group = service.execute
-
- expect(group).to be_persisted
- end
-
- it 'returns the group if it already existed' do
- existing_group = create(:group, path: 'a-group')
-
- expect(service.execute).to eq(existing_group)
- end
-
- it 'raises an error when tring to create a subgroup' do
- service = described_class.new(user, group_path: 'a-group/a-sub-group')
-
- expect { service.execute }.to raise_error('Nested groups are not supported on MySQL')
- end
-
- it_behaves_like 'with a visibility level'
- end
-
- describe 'with subgroups', :nested_groups do
+ describe 'with subgroups' do
let(:params) { { group_path: 'a-group/a-sub-group' } }
describe "#execute" do
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index b5708ebba76..f3af8cf5f3b 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -2,28 +2,13 @@
require 'rails_helper'
-describe Groups::TransferService, :postgresql do
+describe Groups::TransferService do
let(:user) { create(:user) }
let(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let(:transfer_service) { described_class.new(group, user) }
shared_examples 'ensuring allowed transfer for a group' do
- context 'with other database than PostgreSQL' do
- before do
- allow(Group).to receive(:supports_nested_objects?).and_return(false)
- end
-
- it 'returns false' do
- expect(transfer_service.execute(new_parent_group)).to be_falsy
- end
-
- it 'adds an error on group' do
- transfer_service.execute(new_parent_group)
- expect(transfer_service.error).to eq('Transfer failed: Database is not supported.')
- end
- end
-
context "when there's an exception on GitLab shell directories" do
let(:new_parent_group) { create(:group, :public) }
diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb
index d081c20f669..5d4576139f7 100644
--- a/spec/services/groups/update_service_spec.rb
+++ b/spec/services/groups/update_service_spec.rb
@@ -133,7 +133,7 @@ describe Groups::UpdateService do
end
end
- context 'for a subgroup', :nested_groups do
+ context 'for a subgroup' do
let(:subgroup) { create(:group, :private, parent: private_group) }
context 'when the parent group share_with_group_lock is enabled' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 68b79132096..3ae7c7b1c1d 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -116,7 +116,7 @@ describe Issues::UpdateService, :mailer do
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end
- context 'when moving issue between issues from different projects', :nested_groups do
+ context 'when moving issue between issues from different projects' do
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
@@ -703,7 +703,7 @@ describe Issues::UpdateService, :mailer do
end
end
- context 'when moving an issue ', :nested_groups do
+ context 'when moving an issue ' do
it 'raises an error for invalid move ids within a project' do
opts = { move_between_ids: [9000, 9999] }
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 52f9a305d8f..7dce7f035d4 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -267,15 +267,15 @@ describe Members::DestroyService do
expect(group.members.map(&:user)).not_to include(member_user)
end
- it 'removes the subgroup membership', :postgresql do
+ it 'removes the subgroup membership' do
expect(subgroup.members.map(&:user)).not_to include(member_user)
end
- it 'removes the subsubgroup membership', :postgresql do
+ it 'removes the subsubgroup membership' do
expect(subsubgroup.members.map(&:user)).not_to include(member_user)
end
- it 'removes the subsubproject membership', :postgresql do
+ it 'removes the subsubproject membership' do
expect(subsubproject.members.map(&:user)).not_to include(member_user)
end
diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb
index ac40cf02c48..a27fea0c90f 100644
--- a/spec/services/merge_requests/push_options_handler_service_spec.rb
+++ b/spec/services/merge_requests/push_options_handler_service_spec.rb
@@ -11,6 +11,8 @@ describe MergeRequests::PushOptionsHandlerService do
let(:service) { described_class.new(project, user, changes, push_options) }
let(:source_branch) { 'fix' }
let(:target_branch) { 'feature' }
+ let(:title) { 'my title' }
+ let(:description) { 'my description' }
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
let(:deleted_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 #{Gitlab::Git::BLANK_SHA} refs/heads/#{source_branch}" }
@@ -73,6 +75,26 @@ describe MergeRequests::PushOptionsHandlerService do
end
end
+ shared_examples_for 'a service that can set the title of a merge request' do
+ subject(:last_mr) { MergeRequest.last }
+
+ it 'sets the title' do
+ service.execute
+
+ expect(last_mr.title).to eq(title)
+ end
+ end
+
+ shared_examples_for 'a service that can set the description of a merge request' do
+ subject(:last_mr) { MergeRequest.last }
+
+ it 'sets the description' do
+ service.execute
+
+ expect(last_mr.description).to eq(description)
+ end
+ end
+
shared_examples_for 'a service that can set the merge request to merge when pipeline succeeds' do
subject(:last_mr) { MergeRequest.last }
@@ -350,6 +372,138 @@ describe MergeRequests::PushOptionsHandlerService do
end
end
+ describe '`title` push option' do
+ let(:push_options) { { title: title } }
+
+ context 'with a new branch' do
+ let(:changes) { new_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, title: title } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can set the title of a merge request'
+ end
+ end
+
+ context 'with an existing branch but no open MR' do
+ let(:changes) { existing_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, title: title } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can set the title of a merge request'
+ end
+ end
+
+ context 'with an existing branch that has a merge request open' do
+ let(:changes) { existing_branch_changes }
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)}
+
+ it_behaves_like 'a service that does not create a merge request'
+ it_behaves_like 'a service that can set the title of a merge request'
+ end
+
+ context 'with a deleted branch' do
+ let(:changes) { deleted_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+
+ context 'with the project default branch' do
+ let(:changes) { default_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+ end
+
+ describe '`description` push option' do
+ let(:push_options) { { description: description } }
+
+ context 'with a new branch' do
+ let(:changes) { new_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, description: description } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can set the description of a merge request'
+ end
+ end
+
+ context 'with an existing branch but no open MR' do
+ let(:changes) { existing_branch_changes }
+
+ it_behaves_like 'a service that does not create a merge request'
+
+ it 'adds an error to the service' do
+ error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}"
+
+ service.execute
+
+ expect(service.errors).to include(error)
+ end
+
+ context 'when coupled with the `create` push option' do
+ let(:push_options) { { create: true, description: description } }
+
+ it_behaves_like 'a service that can create a merge request'
+ it_behaves_like 'a service that can set the description of a merge request'
+ end
+ end
+
+ context 'with an existing branch that has a merge request open' do
+ let(:changes) { existing_branch_changes }
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)}
+
+ it_behaves_like 'a service that does not create a merge request'
+ it_behaves_like 'a service that can set the description of a merge request'
+ end
+
+ context 'with a deleted branch' do
+ let(:changes) { deleted_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+
+ context 'with the project default branch' do
+ let(:changes) { default_branch_changes }
+
+ it_behaves_like 'a service that does nothing'
+ end
+ end
+
describe 'multiple pushed branches' do
let(:push_options) { { create: true } }
let(:changes) do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index c20de1fd079..1dcade1de0d 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -111,7 +111,7 @@ describe NotificationService, :mailer do
should_email(participant)
end
- context 'for subgroups', :nested_groups do
+ context 'for subgroups' do
before do
build_group(project)
end
@@ -337,7 +337,7 @@ describe NotificationService, :mailer do
it_behaves_like 'new note notifications'
- context 'which is a subgroup', :nested_groups do
+ context 'which is a subgroup' do
let!(:parent) { create(:group) }
let!(:group) { create(:group, parent: parent) }
@@ -388,7 +388,7 @@ describe NotificationService, :mailer do
should_email(admin)
end
- context 'on project that belongs to subgroup', :nested_groups do
+ context 'on project that belongs to subgroup' do
let(:group_reporter) { create(:user) }
let(:group_guest) { create(:user) }
let(:parent_group) { create(:group) }
@@ -458,7 +458,7 @@ describe NotificationService, :mailer do
should_not_email_nested_group_user(@pg_disabled)
end
- it 'notifies parent group members with mention level', :nested_groups do
+ it 'notifies parent group members with mention level' do
note = create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: "@#{@pg_mention.username}")
notification.new_note(note)
@@ -2410,52 +2410,40 @@ describe NotificationService, :mailer do
group
end
- # Creates a nested group only if supported
- # to avoid errors on MySQL
def create_nested_group(visibility)
- if Group.supports_nested_objects?
- parent_group = create(:group, visibility)
- child_group = create(:group, visibility, parent: parent_group)
+ parent_group = create(:group, visibility)
+ child_group = create(:group, visibility, parent: parent_group)
- # Parent group member: global=disabled, parent_group=watch, child_group=global
- @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group)
- @pg_watcher.notification_settings_for(nil).disabled!
+ # Parent group member: global=disabled, parent_group=watch, child_group=global
+ @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group)
+ @pg_watcher.notification_settings_for(nil).disabled!
- # Parent group member: global=global, parent_group=disabled, child_group=global
- @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group)
- @pg_disabled.notification_settings_for(nil).global!
+ # Parent group member: global=global, parent_group=disabled, child_group=global
+ @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group)
+ @pg_disabled.notification_settings_for(nil).global!
- # Parent group member: global=global, parent_group=mention, child_group=global
- @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group)
- @pg_mention.notification_settings_for(nil).global!
+ # Parent group member: global=global, parent_group=mention, child_group=global
+ @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group)
+ @pg_mention.notification_settings_for(nil).global!
- # Parent group member: global=global, parent_group=participating, child_group=global
- @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group)
- @pg_mention.notification_settings_for(nil).global!
+ # Parent group member: global=global, parent_group=participating, child_group=global
+ @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group)
+ @pg_mention.notification_settings_for(nil).global!
- child_group
- else
- create(:group, visibility)
- end
+ child_group
end
def add_member_for_parent_group(user, project)
- return unless Group.supports_nested_objects?
-
project.reload
project.group.parent.add_maintainer(user)
end
def should_email_nested_group_user(user, times: 1, recipients: email_recipients)
- return unless Group.supports_nested_objects?
-
should_email(user, times: 1, recipients: email_recipients)
end
def should_not_email_nested_group_user(user, recipients: email_recipients)
- return unless Group.supports_nested_objects?
-
should_not_email(user, recipients: email_recipients)
end
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index 2f70c8ea94d..b625653bc77 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -118,7 +118,7 @@ describe Projects::AutocompleteService do
expect(milestone_titles).to eq([group_milestone2.title, group_milestone1.title])
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let(:subgroup) { create(:group, :public, parent: group) }
let!(:subgroup_milestone) { create(:milestone, group: subgroup) }
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 95a131e8c86..71c4c3ad0d7 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -574,7 +574,7 @@ describe QuickActions::InterpretService do
context 'Issue' do
it 'populates assignee_ids: [] if content contains /unassign' do
- issue.update(assignee_ids: [developer.id])
+ issue.update!(assignee_ids: [developer.id])
_, updates = service.execute(content, issue)
expect(updates).to eq(assignee_ids: [])
@@ -583,7 +583,7 @@ describe QuickActions::InterpretService do
context 'Merge Request' do
it 'populates assignee_ids: [] if content contains /unassign' do
- merge_request.update(assignee_ids: [developer.id])
+ merge_request.update!(assignee_ids: [developer.id])
_, updates = service.execute(content, merge_request)
expect(updates).to eq(assignee_ids: [])
diff --git a/spec/services/todos/destroy/entity_leave_service_spec.rb b/spec/services/todos/destroy/entity_leave_service_spec.rb
index 2a553e18807..ce809bbf6c5 100644
--- a/spec/services/todos/destroy/entity_leave_service_spec.rb
+++ b/spec/services/todos/destroy/entity_leave_service_spec.rb
@@ -176,7 +176,7 @@ describe Todos::Destroy::EntityLeaveService do
end
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let(:subgroup) { create(:group, :private, parent: group) }
let(:subgroup2) { create(:group, :private, parent: group) }
let(:subproject) { create(:project, group: subgroup) }
diff --git a/spec/services/todos/destroy/group_private_service_spec.rb b/spec/services/todos/destroy/group_private_service_spec.rb
index a1798686d7c..7dd495847b3 100644
--- a/spec/services/todos/destroy/group_private_service_spec.rb
+++ b/spec/services/todos/destroy/group_private_service_spec.rb
@@ -35,7 +35,7 @@ describe Todos::Destroy::GroupPrivateService do
expect(project_member.todos).to match_array([todo_project_member])
end
- context 'with nested groups', :nested_groups do
+ context 'with nested groups' do
let(:parent_group) { create(:group) }
let(:subgroup) { create(:group, :private, parent: group) }
let(:subproject) { create(:project, group: subgroup) }
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index 0287a24808d..f5a914bb482 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -135,7 +135,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
end
- context 'projects of subgroups of groups the user is a member of', :nested_groups do
+ context 'projects of subgroups of groups the user is a member of' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let!(:other_project) { create(:project, group: nested_group) }
@@ -163,7 +163,7 @@ describe Users::RefreshAuthorizedProjectsService do
end
end
- context 'projects shared with subgroups of groups the user is a member of', :nested_groups do
+ context 'projects shared with subgroups of groups the user is a member of' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
let(:other_project) { create(:project) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a44b5069ade..6ef5f46c81b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -258,14 +258,6 @@ RSpec.configure do |config|
Gitlab::CurrentSettings.clear_in_memory_application_settings!
end
- config.around(:each, :nested_groups) do |example|
- example.run if Group.supports_nested_objects?
- end
-
- config.around(:each, :postgresql) do |example|
- example.run if Gitlab::Database.postgresql?
- end
-
# This makes sure the `ApplicationController#can?` method is stubbed with the
# original implementation for all view specs.
config.before(:each, type: :view) do
diff --git a/spec/support/api/boards_shared_examples.rb b/spec/support/api/boards_shared_examples.rb
index 3abb5096a7a..b7aff32460d 100644
--- a/spec/support/api/boards_shared_examples.rb
+++ b/spec/support/api/boards_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'group and project boards' do |route_definition, ee = false|
let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) }
diff --git a/spec/support/api/issues_resolving_discussions_shared_examples.rb b/spec/support/api/issues_resolving_discussions_shared_examples.rb
index d2d6260dfa8..4c44f1bd103 100644
--- a/spec/support/api/issues_resolving_discussions_shared_examples.rb
+++ b/spec/support/api/issues_resolving_discussions_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'creating an issue resolving discussions through the API' do
it 'creates a new project issue' do
expect(response).to have_gitlab_http_status(:created)
diff --git a/spec/support/api/members_shared_examples.rb b/spec/support/api/members_shared_examples.rb
index 8d910e52eda..603efd4fc75 100644
--- a/spec/support/api/members_shared_examples.rb
+++ b/spec/support/api/members_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'a 404 response when source is private' do
before do
source.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb
index 63b719be03e..d6439f77408 100644
--- a/spec/support/api/milestones_shared_examples.rb
+++ b/spec/support/api/milestones_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'group and project milestones' do |route_definition|
let(:resource_route) { "#{route}/#{milestone.id}" }
let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb
index f1341804e56..346015106e3 100644
--- a/spec/support/api/repositories_shared_context.rb
+++ b/spec/support/api/repositories_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'disabled repository' do
before do
project.project_feature.update!(
diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb
index 4cf34d43117..ebbd57c8115 100644
--- a/spec/support/api/schema_matcher.rb
+++ b/spec/support/api/schema_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SchemaPath
def self.expand(schema, dir = nil)
if Gitlab.ee? && dir.nil?
diff --git a/spec/support/api/scopes/read_user_shared_examples.rb b/spec/support/api/scopes/read_user_shared_examples.rb
index 683234264a8..3786a8012f9 100644
--- a/spec/support/api/scopes/read_user_shared_examples.rb
+++ b/spec/support/api/scopes/read_user_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'allows the "read_user" scope' do |api_version|
let(:version) { api_version || 'v4' }
diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb
index 15037222630..3bd1b145433 100644
--- a/spec/support/api/time_tracking_shared_examples.rb
+++ b/spec/support/api/time_tracking_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'an unauthorized API user' do
it { is_expected.to eq(403) }
end
diff --git a/spec/support/banzai/reference_filter_shared_examples.rb b/spec/support/banzai/reference_filter_shared_examples.rb
index 476d80f3a93..27765652f28 100644
--- a/spec/support/banzai/reference_filter_shared_examples.rb
+++ b/spec/support/banzai/reference_filter_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specs for reference links containing HTML.
#
# Requires a reference:
diff --git a/spec/support/batch_loader.rb b/spec/support/batch_loader.rb
index bb790e660a6..0eb8f279d29 100644
--- a/spec/support/batch_loader.rb
+++ b/spec/support/batch_loader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.configure do |config|
config.after do
BatchLoader::Executor.clear_current
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 60990879fe2..8accc5c1df5 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable Style/GlobalVars
require 'capybara/rails'
require 'capybara/rspec'
diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb
index b376822d530..8da55514c78 100644
--- a/spec/support/carrierwave.rb
+++ b/spec/support/carrierwave.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
CarrierWave.root = File.expand_path('tmp/tests/public', Rails.root)
RSpec.configure do |config|
diff --git a/spec/support/chunked_io/chunked_io_helpers.rb b/spec/support/chunked_io/chunked_io_helpers.rb
index fec1f951563..278f577f3cb 100644
--- a/spec/support/chunked_io/chunked_io_helpers.rb
+++ b/spec/support/chunked_io/chunked_io_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ChunkedIOHelpers
def sample_trace_raw
@sample_trace_raw ||= File.read(expand_fixture_path('trace/sample_trace'))
diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb
index efa317fd2f9..a958e259368 100644
--- a/spec/support/commit_trailers_spec_helper.rb
+++ b/spec/support/commit_trailers_spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CommitTrailersSpecHelper
extend ActiveSupport::Concern
diff --git a/spec/support/controllers/githubish_import_controller_shared_context.rb b/spec/support/controllers/githubish_import_controller_shared_context.rb
index e71994edec6..3706178ee34 100644
--- a/spec/support/controllers/githubish_import_controller_shared_context.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'a GitHub-ish import controller' do
let(:user) { create(:user) }
let(:token) { "asdasd12345" }
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index 5bb1269a19d..718d9857b18 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specifications for behavior common to all objects with an email attribute.
# Takes a list of email-format attributes and requires:
# - subject { "the object with a attribute= setter" }
@@ -321,7 +323,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
end
- context 'user has chosen an existing nested namespace and name for the project', :postgresql do
+ context 'user has chosen an existing nested namespace and name for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
@@ -340,7 +342,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
end
- context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do
+ context 'user has chosen a non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
@@ -371,7 +373,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
index a0c77eecb61..d636c1cf6cd 100644
--- a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
+++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_context 'Ldap::OmniauthCallbacksController' do
diff --git a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb
index 355555d9d19..b5149a0fcb1 100644
--- a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb
+++ b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'authenticates sessionless user' do |path, format, params|
params ||= {}
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb
index be1c2bc3046..c57abbd96c6 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/cycle_analytics_helpers/test_generation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# rubocop:disable Metrics/AbcSize
# Note: The ABC size is large here because we have a method generating test cases with
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 08622dff6d9..041ffa25535 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DbCleaner
def delete_from_all_tables!(except: nil)
DatabaseCleaner.clean_with(:deletion, cache_tables: false, except: except)
diff --git a/spec/support/external_authorization_service_helpers.rb b/spec/support/external_authorization_service_helpers.rb
index 79dd9a3d58e..f4214d800cf 100644
--- a/spec/support/external_authorization_service_helpers.rb
+++ b/spec/support/external_authorization_service_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExternalAuthorizationServiceHelpers
def enable_external_authorization_service_check
stub_application_setting(external_authorization_service_enabled: true)
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index 7c8e57702ae..5590bf0fb7e 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'thread comments' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 5d5a0a7b5d2..2f9208e6ed5 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples 'reportable note' do |type|
diff --git a/spec/support/features/resolving_discussions_in_issues_shared_examples.rb b/spec/support/features/resolving_discussions_in_issues_shared_examples.rb
index 8d0e03134d0..d4f8a87d0d8 100644
--- a/spec/support/features/resolving_discussions_in_issues_shared_examples.rb
+++ b/spec/support/features/resolving_discussions_in_issues_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'creating an issue for a thread' do
it 'shows an issue with the title filled in' do
title_field = page.find_field('issue[title]')
diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb
index 0de92aedba5..c97eeba87db 100644
--- a/spec/support/features/rss_shared_examples.rb
+++ b/spec/support/features/rss_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples "an autodiscoverable RSS feed with current_user's feed token" do
it "has an RSS autodiscovery link tag with current_user's feed token" do
expect(page).to have_css("link[type*='atom+xml'][href*='feed_token=#{user.feed_token}']", visible: false)
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 01531864c1f..0f8ad2c6536 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'variable list' do
it 'shows list of variables' do
page.within('.js-ci-variable-list-section') do
diff --git a/spec/support/forgery_protection.rb b/spec/support/forgery_protection.rb
index fa87d5fa881..1d6ea013292 100644
--- a/spec/support/forgery_protection.rb
+++ b/spec/support/forgery_protection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ForgeryProtection
def with_forgery_protection
ActionController::Base.allow_forgery_protection = true
diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb
index 2fdbddd40c2..a1328ef0d13 100644
--- a/spec/support/google_api/cloud_platform_helpers.rb
+++ b/spec/support/google_api/cloud_platform_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GoogleApi
module CloudPlatformHelpers
def stub_google_api_validate_token
diff --git a/spec/support/helpers/api_helpers.rb b/spec/support/helpers/api_helpers.rb
index 4a9ce9beb78..aff0f87b6e4 100644
--- a/spec/support/helpers/api_helpers.rb
+++ b/spec/support/helpers/api_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ApiHelpers
# Public: Prepend a request path with the path to the API
#
@@ -30,11 +32,12 @@ module ApiHelpers
end
if query_string
- full_path << (path.index('?') ? '&' : '?')
- full_path << query_string
- end
+ separator = path.index('?') ? '&' : '?'
- full_path
+ full_path + separator + query_string
+ else
+ full_path
+ end
end
def expect_paginated_array_response(items)
diff --git a/spec/support/helpers/assets_helpers.rb b/spec/support/helpers/assets_helpers.rb
index 09bbf451671..fa24ad9ad2a 100644
--- a/spec/support/helpers/assets_helpers.rb
+++ b/spec/support/helpers/assets_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module AssetsHelpers
# In a CI environment the assets are not compiled, as there is a CI job
# `compile-assets` that compiles them in the prepare stage for all following
diff --git a/spec/support/helpers/bare_repo_operations.rb b/spec/support/helpers/bare_repo_operations.rb
index 3f4a4243cb6..099610f087d 100644
--- a/spec/support/helpers/bare_repo_operations.rb
+++ b/spec/support/helpers/bare_repo_operations.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'zlib'
class BareRepoOperations
diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb
index b85fde222ea..683ee3e4bf2 100644
--- a/spec/support/helpers/board_helpers.rb
+++ b/spec/support/helpers/board_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module BoardHelpers
def click_card(card)
within card do
diff --git a/spec/support/helpers/capybara_helpers.rb b/spec/support/helpers/capybara_helpers.rb
index bcc2df44708..5abbc1e2951 100644
--- a/spec/support/helpers/capybara_helpers.rb
+++ b/spec/support/helpers/capybara_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CapybaraHelpers
# Execute a block a certain number of times before considering it a failure
#
diff --git a/spec/support/helpers/ci_artifact_metadata_generator.rb b/spec/support/helpers/ci_artifact_metadata_generator.rb
index ef638d59d2d..e02501565a9 100644
--- a/spec/support/helpers/ci_artifact_metadata_generator.rb
+++ b/spec/support/helpers/ci_artifact_metadata_generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# frozen_sting_literal: true
# This generates fake CI metadata .gz for testing
diff --git a/spec/support/helpers/cookie_helper.rb b/spec/support/helpers/cookie_helper.rb
index 5ff7b0b68c9..ea4be12355b 100644
--- a/spec/support/helpers/cookie_helper.rb
+++ b/spec/support/helpers/cookie_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Helper for setting cookies in Selenium/WebDriver
#
module CookieHelper
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 100e439ef44..575b2e779c5 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module CycleAnalyticsHelpers
include GitHelpers
diff --git a/spec/support/helpers/database_connection_helpers.rb b/spec/support/helpers/database_connection_helpers.rb
index 763329499f0..10ea7b5de91 100644
--- a/spec/support/helpers/database_connection_helpers.rb
+++ b/spec/support/helpers/database_connection_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DatabaseConnectionHelpers
def run_with_new_database_connection
pool = ActiveRecord::Base.connection_pool
diff --git a/spec/support/helpers/devise_helpers.rb b/spec/support/helpers/devise_helpers.rb
index fb2a110422a..70fc6a48414 100644
--- a/spec/support/helpers/devise_helpers.rb
+++ b/spec/support/helpers/devise_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DeviseHelpers
# explicitly tells Devise which mapping to use
# this is needed when we are testing a Devise controller bypassing the router
diff --git a/spec/support/helpers/drag_to_helper.rb b/spec/support/helpers/drag_to_helper.rb
index 6d53ad0b602..6099f87323f 100644
--- a/spec/support/helpers/drag_to_helper.rb
+++ b/spec/support/helpers/drag_to_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DragTo
def drag_to(list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0, selector: '', scrollable: 'body', duration: 1000)
evaluate_script("simulateDrag({scrollable: $('#{scrollable}').get(0), duration: #{duration}, from: {el: $('#{selector}').eq(#{list_from_index}).get(0), index: #{from_index}}, to: {el: $('#{selector}').eq(#{list_to_index}).get(0), index: #{to_index}}});")
diff --git a/spec/support/helpers/dropzone_helper.rb b/spec/support/helpers/dropzone_helper.rb
index fe72d320fcf..a0f261b312e 100644
--- a/spec/support/helpers/dropzone_helper.rb
+++ b/spec/support/helpers/dropzone_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DropzoneHelper
# Provides a way to perform `attach_file` for a Dropzone-based file input
#
diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index ed049daba80..83ba654fab3 100644
--- a/spec/support/helpers/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module EmailHelpers
def sent_to_user(user, recipients: email_recipients)
recipients.count { |to| to == user.notification_email }
diff --git a/spec/support/helpers/exclusive_lease_helpers.rb b/spec/support/helpers/exclusive_lease_helpers.rb
index 383cc7dee81..77703e20602 100644
--- a/spec/support/helpers/exclusive_lease_helpers.rb
+++ b/spec/support/helpers/exclusive_lease_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExclusiveLeaseHelpers
def stub_exclusive_lease(key = nil, uuid = 'uuid', renew: false, timeout: nil)
key ||= instance_of(String)
diff --git a/spec/support/helpers/expect_next_instance_of.rb b/spec/support/helpers/expect_next_instance_of.rb
index b95046b2b42..749d2cb2a56 100644
--- a/spec/support/helpers/expect_next_instance_of.rb
+++ b/spec/support/helpers/expect_next_instance_of.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExpectNextInstanceOf
def expect_next_instance_of(klass, *new_args)
receive_new = receive(:new)
diff --git a/spec/support/helpers/expect_offense.rb b/spec/support/helpers/expect_offense.rb
index 35718ba90c5..76301fe19ff 100644
--- a/spec/support/helpers/expect_offense.rb
+++ b/spec/support/helpers/expect_offense.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rubocop/rspec/support'
# https://github.com/backus/rubocop-rspec/blob/master/spec/support/expect_offense.rb
diff --git a/spec/support/helpers/fake_blob_helpers.rb b/spec/support/helpers/fake_blob_helpers.rb
index 801ca8b7412..ef4740638ff 100644
--- a/spec/support/helpers/fake_blob_helpers.rb
+++ b/spec/support/helpers/fake_blob_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FakeBlobHelpers
class FakeBlob
include BlobLike
diff --git a/spec/support/helpers/fake_migration_classes.rb b/spec/support/helpers/fake_migration_classes.rb
index c7766df7a52..6c066b3b199 100644
--- a/spec/support/helpers/fake_migration_classes.rb
+++ b/spec/support/helpers/fake_migration_classes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration[4.2]
include Gitlab::Database::RenameReservedPathsMigration::V1
diff --git a/spec/support/helpers/fake_u2f_device.rb b/spec/support/helpers/fake_u2f_device.rb
index 22cd8152d77..f765b277175 100644
--- a/spec/support/helpers/fake_u2f_device.rb
+++ b/spec/support/helpers/fake_u2f_device.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FakeU2fDevice
attr_reader :name
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index df88fd425c9..2a50b41cb4e 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These helpers allow you to manipulate with sorting features.
#
# Usage:
diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb
index 8a139fafac2..a2d8d71b541 100644
--- a/spec/support/helpers/features/notes_helpers.rb
+++ b/spec/support/helpers/features/notes_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These helpers allow you to manipulate with notes.
#
# Usage:
diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb
index 003ecb251fe..a6428bf8573 100644
--- a/spec/support/helpers/features/sorting_helpers.rb
+++ b/spec/support/helpers/features/sorting_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These helpers allow you to manipulate with sorting features.
#
# Usage:
diff --git a/spec/support/helpers/filter_spec_helper.rb b/spec/support/helpers/filter_spec_helper.rb
index 721d359c2ee..95c24d76dcd 100644
--- a/spec/support/helpers/filter_spec_helper.rb
+++ b/spec/support/helpers/filter_spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Helper methods for Banzai filter specs
#
# Must be included into specs manually
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 34ef185ea27..39c818b1763 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FilteredSearchHelpers
def filtered_search
page.find('.filtered-search')
diff --git a/spec/support/helpers/fixture_helpers.rb b/spec/support/helpers/fixture_helpers.rb
index 611d19f36a0..7b3b8ae5f7a 100644
--- a/spec/support/helpers/fixture_helpers.rb
+++ b/spec/support/helpers/fixture_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module FixtureHelpers
def fixture_file(filename, dir: '')
return '' if filename.blank?
diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb
index c83860d7b51..de8bb9ac8e3 100644
--- a/spec/support/helpers/git_http_helpers.rb
+++ b/spec/support/helpers/git_http_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'workhorse_helpers'
module GitHttpHelpers
diff --git a/spec/support/helpers/gitlab_verify_helpers.rb b/spec/support/helpers/gitlab_verify_helpers.rb
index 5df4bf24ec2..9901ce374ed 100644
--- a/spec/support/helpers/gitlab_verify_helpers.rb
+++ b/spec/support/helpers/gitlab_verify_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GitlabVerifyHelpers
def collect_ranges(args = {})
verifier = described_class.new(args.merge(batch_size: 1))
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index ec3c460cd37..ae1b859ae3f 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GraphqlHelpers
MutationDefinition = Struct.new(:query, :variables)
diff --git a/spec/support/helpers/import_spec_helper.rb b/spec/support/helpers/import_spec_helper.rb
index d4eced724fa..d8fb2ba08af 100644
--- a/spec/support/helpers/import_spec_helper.rb
+++ b/spec/support/helpers/import_spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'ostruct'
# Helper methods for controller specs in the Import namespace
diff --git a/spec/support/helpers/input_helper.rb b/spec/support/helpers/input_helper.rb
index acbb42274ec..5136f8e9cb8 100644
--- a/spec/support/helpers/input_helper.rb
+++ b/spec/support/helpers/input_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# see app/assets/javascripts/test_utils/simulate_input.js
module InputHelper
diff --git a/spec/support/helpers/inspect_requests.rb b/spec/support/helpers/inspect_requests.rb
index 88ddc5c7f6c..4a1d9cb8539 100644
--- a/spec/support/helpers/inspect_requests.rb
+++ b/spec/support/helpers/inspect_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative './wait_for_requests'
module InspectRequests
diff --git a/spec/support/helpers/issue_helpers.rb b/spec/support/helpers/issue_helpers.rb
index ffd72515f37..82b954a92e2 100644
--- a/spec/support/helpers/issue_helpers.rb
+++ b/spec/support/helpers/issue_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module IssueHelpers
def visit_issues(project, opts = {})
visit project_issues_path project, opts
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index e9129bd263e..7ec65318ec5 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'action_dispatch/testing/test_request'
require 'fileutils'
diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb
index 7e955f3d593..57c33c81ea3 100644
--- a/spec/support/helpers/jira_service_helper.rb
+++ b/spec/support/helpers/jira_service_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module JiraServiceHelper
JIRA_URL = "http://jira.example.net".freeze
JIRA_API = JIRA_URL + "/rest/api/2"
diff --git a/spec/support/helpers/key_generator_helper.rb b/spec/support/helpers/key_generator_helper.rb
index 768b8c127b9..59c8eeb3692 100644
--- a/spec/support/helpers/key_generator_helper.rb
+++ b/spec/support/helpers/key_generator_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Spec
module Support
module Helpers
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index 278264f3df5..538a5b8ef3c 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module KubernetesHelpers
include Gitlab::Kubernetes
diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb
index 66ca5d7f0a3..dce8a3803f5 100644
--- a/spec/support/helpers/ldap_helpers.rb
+++ b/spec/support/helpers/ldap_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module LdapHelpers
def ldap_adapter(provider = 'ldapmain', ldap = double(:ldap))
::Gitlab::Auth::LDAP::Adapter.new(provider, ldap)
diff --git a/spec/support/helpers/live_debugger.rb b/spec/support/helpers/live_debugger.rb
index 911eb48a8ca..d6091035b59 100644
--- a/spec/support/helpers/live_debugger.rb
+++ b/spec/support/helpers/live_debugger.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'io/console'
module LiveDebugger
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 0cb99b4e087..2b508ee6f2c 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'devise_helpers'
module LoginHelpers
diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 96401379cf0..eea03fb9325 100644
--- a/spec/support/helpers/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This is a helper class used by the GitLab Markdown feature spec
#
# Because the feature spec only cares about the output of the Markdown, and the
diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb
index 3b49d0b3319..49beecc6d4b 100644
--- a/spec/support/helpers/merge_request_diff_helpers.rb
+++ b/spec/support/helpers/merge_request_diff_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequestDiffHelpers
def click_diff_line(line_holder, diff_side = nil)
line = get_line_components(line_holder, diff_side)
diff --git a/spec/support/helpers/merge_request_helpers.rb b/spec/support/helpers/merge_request_helpers.rb
index 772adff4626..3c359bc9353 100644
--- a/spec/support/helpers/merge_request_helpers.rb
+++ b/spec/support/helpers/merge_request_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MergeRequestHelpers
def visit_merge_requests(project, opts = {})
visit project_merge_requests_path project, opts
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 272b24f7541..2727ab7fb1e 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MigrationsHelpers
def active_record_base
ActiveRecord::Base
diff --git a/spec/support/helpers/mobile_helpers.rb b/spec/support/helpers/mobile_helpers.rb
index 4230d315d9b..94dbd2fb1b7 100644
--- a/spec/support/helpers/mobile_helpers.rb
+++ b/spec/support/helpers/mobile_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MobileHelpers
def resize_screen_xs
resize_window(575, 768)
diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb
index 79a0aa174b1..a4322618cd3 100644
--- a/spec/support/helpers/note_interaction_helpers.rb
+++ b/spec/support/helpers/note_interaction_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NoteInteractionHelpers
def open_more_actions_dropdown(note)
note_element = find("#note_#{note.id}")
diff --git a/spec/support/helpers/notification_helpers.rb b/spec/support/helpers/notification_helpers.rb
index 44c2051598c..16ecb338f6e 100644
--- a/spec/support/helpers/notification_helpers.rb
+++ b/spec/support/helpers/notification_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module NotificationHelpers
extend self
diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index bcb11a09b36..b2d22853e4c 100644
--- a/spec/support/helpers/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ProjectForksHelper
def fork_project(project, user = nil, params = {})
Gitlab::GitalyClient.allow_n_plus_1_calls do
diff --git a/spec/support/helpers/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb
index db662836013..7c03746a395 100644
--- a/spec/support/helpers/prometheus_helpers.rb
+++ b/spec/support/helpers/prometheus_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module PrometheusHelpers
def prometheus_memory_query(environment_slug)
%{avg(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}) / 2^20}
@@ -74,6 +76,14 @@ module PrometheusHelpers
WebMock.stub_request(:any, /prometheus.example.com/)
end
+ def stub_any_prometheus_request_with_response(status: 200, headers: {}, body: nil)
+ stub_any_prometheus_request.to_return({
+ status: status,
+ headers: { 'Content-Type' => 'application/json' }.merge(headers),
+ body: body || prometheus_values_body.to_json
+ })
+ end
+
def stub_all_prometheus_requests(environment_slug, body: nil, status: 200)
stub_prometheus_request(
prometheus_query_with_time_url(prometheus_memory_query(environment_slug), Time.now.utc),
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
index f77b43391dd..d936dc6de41 100644
--- a/spec/support/helpers/query_recorder.rb
+++ b/spec/support/helpers/query_recorder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveRecord
class QueryRecorder
attr_reader :log, :skip_cached, :cached
diff --git a/spec/support/helpers/quick_actions_helpers.rb b/spec/support/helpers/quick_actions_helpers.rb
index 361190aa352..cb853f5363f 100644
--- a/spec/support/helpers/quick_actions_helpers.rb
+++ b/spec/support/helpers/quick_actions_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module QuickActionsHelpers
def write_note(text)
Sidekiq::Testing.fake! do
diff --git a/spec/support/helpers/rake_helpers.rb b/spec/support/helpers/rake_helpers.rb
index 7d8d7750bf3..d8f354a69da 100644
--- a/spec/support/helpers/rake_helpers.rb
+++ b/spec/support/helpers/rake_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RakeHelpers
def run_rake_task(task_name, *args)
Rake::Task[task_name].reenable
diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index 528da37e8cf..aa9d3b3a199 100644
--- a/spec/support/helpers/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ReactiveCachingHelpers
def reactive_cache_key(subject, *qualifiers)
([subject.class.reactive_cache_key.call(subject)].flatten + qualifiers).join(':')
diff --git a/spec/support/helpers/redis_without_keys.rb b/spec/support/helpers/redis_without_keys.rb
index 6220167dee6..e030f1028f7 100644
--- a/spec/support/helpers/redis_without_keys.rb
+++ b/spec/support/helpers/redis_without_keys.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Redis
ForbiddenCommand = Class.new(StandardError)
diff --git a/spec/support/helpers/reference_parser_helpers.rb b/spec/support/helpers/reference_parser_helpers.rb
index 9f27502aa52..f96a01d15b5 100644
--- a/spec/support/helpers/reference_parser_helpers.rb
+++ b/spec/support/helpers/reference_parser_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ReferenceParserHelpers
def empty_html_link
Nokogiri::HTML.fragment('<a></a>').children[0]
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index 44d95a029af..ca4d2acbf2c 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RepoHelpers
extend self
diff --git a/spec/support/helpers/routes_helpers.rb b/spec/support/helpers/routes_helpers.rb
index c4129606418..2a7cd81cbe3 100644
--- a/spec/support/helpers/routes_helpers.rb
+++ b/spec/support/helpers/routes_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RoutesHelpers
def fake_routes(&block)
@routes = @routes.dup
diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb
index abbbb636d66..815337f8615 100644
--- a/spec/support/helpers/search_helpers.rb
+++ b/spec/support/helpers/search_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SearchHelpers
def select_filter(name)
find(:xpath, "//ul[contains(@class, 'search-filter')]//a[contains(.,'#{name}')]").click
diff --git a/spec/support/helpers/seed_repo.rb b/spec/support/helpers/seed_repo.rb
index 71f1a86b0c1..20738b45129 100644
--- a/spec/support/helpers/seed_repo.rb
+++ b/spec/support/helpers/seed_repo.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This file is generated by generate-seed-repo-rb. Do not edit this file manually.
#
# Seed repo:
diff --git a/spec/support/helpers/select2_helper.rb b/spec/support/helpers/select2_helper.rb
index 87672c8896d..9c42c2b0d8b 100644
--- a/spec/support/helpers/select2_helper.rb
+++ b/spec/support/helpers/select2_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'wait_for_requests'
# Select2 ajax programmatic helper
diff --git a/spec/support/helpers/selection_helper.rb b/spec/support/helpers/selection_helper.rb
index b4725b137b2..a5f9ca76f6e 100644
--- a/spec/support/helpers/selection_helper.rb
+++ b/spec/support/helpers/selection_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SelectionHelper
def select_element(selector)
find(selector)
diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb
index e505a6b7258..3801d25fb63 100644
--- a/spec/support/helpers/sorting_helper.rb
+++ b/spec/support/helpers/sorting_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Helper allows you to sort items
#
# Params
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 049702be1f6..c8b2bf040e6 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'active_support/core_ext/hash/transform_values'
require 'active_support/hash_with_indifferent_access'
require 'active_support/dependencies'
diff --git a/spec/support/helpers/stub_env.rb b/spec/support/helpers/stub_env.rb
index 1c2f474a015..8107ffc939f 100644
--- a/spec/support/helpers/stub_env.rb
+++ b/spec/support/helpers/stub_env.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubENV
def stub_env(key_or_hash, value = nil)
diff --git a/spec/support/helpers/stub_feature_flags.rb b/spec/support/helpers/stub_feature_flags.rb
index 48258692304..6c3efff7262 100644
--- a/spec/support/helpers/stub_feature_flags.rb
+++ b/spec/support/helpers/stub_feature_flags.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubFeatureFlags
# Stub Feature flags with `flag_name: true/false`
#
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index 4cb3b18df85..badea94352a 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubGitlabCalls
def stub_gitlab_calls
stub_user
diff --git a/spec/support/helpers/stub_gitlab_data.rb b/spec/support/helpers/stub_gitlab_data.rb
index fa402f35b95..ed518393c03 100644
--- a/spec/support/helpers/stub_gitlab_data.rb
+++ b/spec/support/helpers/stub_gitlab_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubGitlabData
def gitlab_ci_yaml
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
diff --git a/spec/support/helpers/stub_metrics.rb b/spec/support/helpers/stub_metrics.rb
index 64983fdf222..e347955efbb 100644
--- a/spec/support/helpers/stub_metrics.rb
+++ b/spec/support/helpers/stub_metrics.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubMetrics
def authentication_metrics
Gitlab::Auth::Activity
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index d31f9908714..e5b8bb712bb 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubObjectStorage
def stub_object_storage_uploader(
config:,
diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb
index 5cad35282c0..6eb8007ed26 100644
--- a/spec/support/helpers/stub_requests.rb
+++ b/spec/support/helpers/stub_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module StubRequests
IP_ADDRESS_STUB = '8.8.8.9'.freeze
diff --git a/spec/support/helpers/stub_worker.rb b/spec/support/helpers/stub_worker.rb
index 58b7ee93dff..cac839ed5fe 100644
--- a/spec/support/helpers/stub_worker.rb
+++ b/spec/support/helpers/stub_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubWorker
def stub_worker(queue:)
diff --git a/spec/support/helpers/terms_helper.rb b/spec/support/helpers/terms_helper.rb
index a00ec14138b..a61bae18f9a 100644
--- a/spec/support/helpers/terms_helper.rb
+++ b/spec/support/helpers/terms_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TermsHelper
def enforce_terms
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index b062631b995..a4acf76e1a3 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rspec/mocks'
require 'toml-rb'
diff --git a/spec/support/helpers/upload_helpers.rb b/spec/support/helpers/upload_helpers.rb
index 5eead80c935..60e14a8673b 100644
--- a/spec/support/helpers/upload_helpers.rb
+++ b/spec/support/helpers/upload_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'fileutils'
module UploadHelpers
diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb
index 45b9faa0fea..3bb2f7c5b51 100644
--- a/spec/support/helpers/wait_for_requests.rb
+++ b/spec/support/helpers/wait_for_requests.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WaitForRequests
extend self
diff --git a/spec/support/helpers/wait_helpers.rb b/spec/support/helpers/wait_helpers.rb
index 7e8e25798e8..a8c4408db59 100644
--- a/spec/support/helpers/wait_helpers.rb
+++ b/spec/support/helpers/wait_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WaitHelpers
extend self
diff --git a/spec/support/helpers/wiki_helpers.rb b/spec/support/helpers/wiki_helpers.rb
index 8165403cb60..06cea728b42 100644
--- a/spec/support/helpers/wiki_helpers.rb
+++ b/spec/support/helpers/wiki_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WikiHelpers
extend self
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index ef1f9f68671..4488e5f227e 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module WorkhorseHelpers
extend self
diff --git a/spec/support/http_io/http_io_helpers.rb b/spec/support/http_io/http_io_helpers.rb
index 42144870eb5..0193db81fa9 100644
--- a/spec/support/http_io/http_io_helpers.rb
+++ b/spec/support/http_io/http_io_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module HttpIOHelpers
def stub_remote_url_206(url, file_path)
WebMock.stub_request(:get, url)
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index 2542a59bb00..ac6840dbcfc 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ImportExport
module CommonUtil
def setup_symlink(tmpdir, symlink_name)
diff --git a/spec/support/import_export/configuration_helper.rb b/spec/support/import_export/configuration_helper.rb
index b4164cff922..122df7f27f0 100644
--- a/spec/support/import_export/configuration_helper.rb
+++ b/spec/support/import_export/configuration_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConfigurationHelper
# Returns a list of models from hashes/arrays contained in +project_tree+
def names_from_tree(project_tree)
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 388b88f0331..f862a9bc1a4 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require './spec/support/import_export/configuration_helper'
module ExportFileHelper
diff --git a/spec/support/inspect_squelch.rb b/spec/support/inspect_squelch.rb
index 8ee6732370b..90475204889 100644
--- a/spec/support/inspect_squelch.rb
+++ b/spec/support/inspect_squelch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class can generate a lot of output if it fails,
# so squelch the instance variable output.
class ActiveSupport::Cache::NullStore
diff --git a/spec/support/issuables_requiring_filter_shared_examples.rb b/spec/support/issuables_requiring_filter_shared_examples.rb
index 71bcc82ee58..ee25df00dfb 100644
--- a/spec/support/issuables_requiring_filter_shared_examples.rb
+++ b/spec/support/issuables_requiring_filter_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuables requiring filter' do |action|
it "doesn't load any issuables if no filter is set" do
expect_any_instance_of(described_class).not_to receive(:issuables_collection)
diff --git a/spec/support/json_response.rb b/spec/support/json_response.rb
index 43d8ab73dde..55bdce0cfe9 100644
--- a/spec/support/json_response.rb
+++ b/spec/support/json_response.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.configure do |config|
config.include_context 'JSON response', type: :controller
config.include_context 'JSON response', type: :request
diff --git a/spec/support/matchers/abort_matcher.rb b/spec/support/matchers/abort_matcher.rb
index ce1dd140210..64fed2ca069 100644
--- a/spec/support/matchers/abort_matcher.rb
+++ b/spec/support/matchers/abort_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :abort_execution do
match do |code_block|
@captured_stderr = StringIO.new
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index e6899e2d23c..c9ff777f604 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# AccessMatchers
#
# The custom matchers contained in this module are used to test a user's access
diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb
index 429401a5da8..401bf6c196e 100644
--- a/spec/support/matchers/access_matchers_for_controller.rb
+++ b/spec/support/matchers/access_matchers_for_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# AccessMatchersForController
#
# For testing authorize_xxx in controller.
diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb
index f4127efc6ae..c38aa7ad6a6 100644
--- a/spec/support/matchers/background_migrations_matchers.rb
+++ b/spec/support/matchers/background_migrations_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_scheduled_delayed_migration do |delay, *expected|
match do |migration|
BackgroundMigrationWorker.jobs.any? do |job|
diff --git a/spec/support/matchers/be_a_binary_string.rb b/spec/support/matchers/be_a_binary_string.rb
index f041ae76167..6195c6c7554 100644
--- a/spec/support/matchers/be_a_binary_string.rb
+++ b/spec/support/matchers/be_a_binary_string.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_a_binary_string do |_|
match do |actual|
actual.is_a?(String) && actual.encoding == Encoding.find('ASCII-8BIT')
diff --git a/spec/support/matchers/be_like_time.rb b/spec/support/matchers/be_like_time.rb
index 1f27390eab7..b449f7a7ffb 100644
--- a/spec/support/matchers/be_like_time.rb
+++ b/spec/support/matchers/be_like_time.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_like_time do |expected|
match do |actual|
expect(actual).to be_within(1.second).of(expected)
diff --git a/spec/support/matchers/be_url.rb b/spec/support/matchers/be_url.rb
index f8096af1b22..7bd0e7fada4 100644
--- a/spec/support/matchers/be_url.rb
+++ b/spec/support/matchers/be_url.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_url do |_|
match do |actual|
URI.parse(actual) rescue false
diff --git a/spec/support/matchers/be_utf8.rb b/spec/support/matchers/be_utf8.rb
index ea806352422..4fa55539a49 100644
--- a/spec/support/matchers/be_utf8.rb
+++ b/spec/support/matchers/be_utf8.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_utf8 do |_|
match do |actual|
actual.is_a?(String) && actual.encoding == Encoding.find('UTF-8')
diff --git a/spec/support/matchers/be_valid_commit.rb b/spec/support/matchers/be_valid_commit.rb
index 3696e4d5f03..b59339de622 100644
--- a/spec/support/matchers/be_valid_commit.rb
+++ b/spec/support/matchers/be_valid_commit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :be_valid_commit do
match do |actual|
actual &&
diff --git a/spec/support/matchers/disallow_request_matchers.rb b/spec/support/matchers/disallow_request_matchers.rb
index db4d90e4fd0..a161e3660cd 100644
--- a/spec/support/matchers/disallow_request_matchers.rb
+++ b/spec/support/matchers/disallow_request_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :disallow_request do
match do |middleware|
alert = middleware.env['rack.session'].to_hash
diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb
index cd042401f3a..40cf85eb8e5 100644
--- a/spec/support/matchers/exceed_query_limit.rb
+++ b/spec/support/matchers/exceed_query_limit.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ExceedQueryLimitHelpers
def with_threshold(threshold)
@threshold = threshold
diff --git a/spec/support/matchers/execute_check.rb b/spec/support/matchers/execute_check.rb
index 7232fad52fb..d3c0751f0dc 100644
--- a/spec/support/matchers/execute_check.rb
+++ b/spec/support/matchers/execute_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :execute_check do |expected|
match do |actual|
expect(actual).to eq(SystemCheck)
diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb
index 933ed22b5d0..c2b3ebe3422 100644
--- a/spec/support/matchers/gitaly_matchers.rb
+++ b/spec/support/matchers/gitaly_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :gitaly_request_with_path do |storage_name, relative_path|
match do |actual|
repository = actual.repository
diff --git a/spec/support/matchers/gitlab_git_matchers.rb b/spec/support/matchers/gitlab_git_matchers.rb
index c840cd4bf2d..aea1603db05 100644
--- a/spec/support/matchers/gitlab_git_matchers.rb
+++ b/spec/support/matchers/gitlab_git_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :gitlab_git_repository_with do |values|
match do |actual|
actual.is_a?(Gitlab::Git::Repository) &&
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index 7894484f590..4d48b4b5389 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :require_graphql_authorizations do |*expected|
match do |field|
expect(field.metadata[:authorize]).to eq(*expected)
diff --git a/spec/support/matchers/have_emoji.rb b/spec/support/matchers/have_emoji.rb
index 23fb8e9c1c4..273bd0b7f40 100644
--- a/spec/support/matchers/have_emoji.rb
+++ b/spec/support/matchers/have_emoji.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :have_emoji do |emoji_name|
match do |actual|
expect(actual).to have_selector("gl-emoji[data-name='#{emoji_name}']")
diff --git a/spec/support/matchers/have_gitlab_http_status.rb b/spec/support/matchers/have_gitlab_http_status.rb
index e7e418cdde4..13a64a58218 100644
--- a/spec/support/matchers/have_gitlab_http_status.rb
+++ b/spec/support/matchers/have_gitlab_http_status.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :have_gitlab_http_status do |expected|
match do |actual|
expect(actual).to have_http_status(expected)
diff --git a/spec/support/matchers/have_issuable_counts.rb b/spec/support/matchers/have_issuable_counts.rb
index 92cf3de5448..049cfc022fb 100644
--- a/spec/support/matchers/have_issuable_counts.rb
+++ b/spec/support/matchers/have_issuable_counts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :have_issuable_counts do |opts|
expected_counts = opts.map do |state, count|
"#{state.to_s.humanize} #{count}"
diff --git a/spec/support/matchers/include_module.rb b/spec/support/matchers/include_module.rb
index 0a78af1e90e..9b6970cf061 100644
--- a/spec/support/matchers/include_module.rb
+++ b/spec/support/matchers/include_module.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :include_module do |expected|
match do
described_class.included_modules.include?(expected)
diff --git a/spec/support/matchers/issuable_matchers.rb b/spec/support/matchers/issuable_matchers.rb
index 62f510b0fbd..743f0b8c932 100644
--- a/spec/support/matchers/issuable_matchers.rb
+++ b/spec/support/matchers/issuable_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :have_header_with_correct_id_and_link do |level, text, id, parent = ".md"|
match do |actual|
node = find("#{parent} h#{level} a#user-content-#{id}")
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index ec4ec6f4038..12e8fa83a60 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# MarkdownMatchers
#
# Custom matchers for our custom HTML::Pipeline filters. These are used to test
diff --git a/spec/support/matchers/match_file.rb b/spec/support/matchers/match_file.rb
index d1888b3376a..4e522b52912 100644
--- a/spec/support/matchers/match_file.rb
+++ b/spec/support/matchers/match_file.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :match_file do |expected|
match do |actual|
expect(Digest::MD5.hexdigest(actual)).to eq(Digest::MD5.hexdigest(File.read(expected)))
diff --git a/spec/support/matchers/match_ids.rb b/spec/support/matchers/match_ids.rb
index 1cb6b74acac..7bc41949937 100644
--- a/spec/support/matchers/match_ids.rb
+++ b/spec/support/matchers/match_ids.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :match_ids do |*expected|
match do |actual|
actual_ids = map_ids(actual)
diff --git a/spec/support/matchers/metric_counter_matcher.rb b/spec/support/matchers/metric_counter_matcher.rb
index 22d5cd17e3f..f0d52b9b149 100644
--- a/spec/support/matchers/metric_counter_matcher.rb
+++ b/spec/support/matchers/metric_counter_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :increment do |counter|
match do |adapter|
expect(adapter.send(counter))
diff --git a/spec/support/matchers/navigation_matcher.rb b/spec/support/matchers/navigation_matcher.rb
index 63f59b9654c..ad73c96031e 100644
--- a/spec/support/matchers/navigation_matcher.rb
+++ b/spec/support/matchers/navigation_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :have_active_navigation do |expected|
match do |page|
expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1)
diff --git a/spec/support/matchers/pagination_matcher.rb b/spec/support/matchers/pagination_matcher.rb
index 9a7697e2bfc..a3e9c3b8474 100644
--- a/spec/support/matchers/pagination_matcher.rb
+++ b/spec/support/matchers/pagination_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :include_pagination_headers do |expected|
match do |actual|
expect(actual.headers).to include('X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page', 'Link')
diff --git a/spec/support/matchers/query_matcher.rb b/spec/support/matchers/query_matcher.rb
index bb0feca7c43..3e47fe241bc 100644
--- a/spec/support/matchers/query_matcher.rb
+++ b/spec/support/matchers/query_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :make_queries_matching do |matcher, expected_count = nil|
supports_block_expectations
diff --git a/spec/support/matchers/satisfy_matchers.rb b/spec/support/matchers/satisfy_matchers.rb
index 585915bac93..dd1920ee2b2 100644
--- a/spec/support/matchers/satisfy_matchers.rb
+++ b/spec/support/matchers/satisfy_matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These matchers are a syntactic hack to provide more readable expectations for
# an Enumerable object.
#
diff --git a/spec/support/matchers/security_header_matcher.rb b/spec/support/matchers/security_header_matcher.rb
index f8518d13ebb..760b1fddd06 100644
--- a/spec/support/matchers/security_header_matcher.rb
+++ b/spec/support/matchers/security_header_matcher.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec::Matchers.define :include_security_headers do |expected|
match do |actual|
expect(actual.headers).to include('X-Content-Type-Options')
diff --git a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
index 016bcfa9b1b..656be3b6d4d 100644
--- a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
+++ b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module MigrationsHelpers
module TrackUntrackedUploadsHelpers
PUBLIC_DIR = File.join(Rails.root, 'tmp', 'tests', 'public')
diff --git a/spec/support/omni_auth.rb b/spec/support/omni_auth.rb
index 0b1af4052ff..64aa3855a6f 100644
--- a/spec/support/omni_auth.rb
+++ b/spec/support/omni_auth.rb
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
OmniAuth.config.test_mode = true
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index de21e808932..82582630dee 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'additional metrics query' do
include Prometheus::MetricBuilders
diff --git a/spec/support/prometheus/metric_builders.rb b/spec/support/prometheus/metric_builders.rb
index c8d056d3fc8..512e32a44d0 100644
--- a/spec/support/prometheus/metric_builders.rb
+++ b/spec/support/prometheus/metric_builders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Prometheus
module MetricBuilders
def simple_query(suffix = 'a', **opts)
diff --git a/spec/support/protected_tags/access_control_ce_shared_examples.rb b/spec/support/protected_tags/access_control_ce_shared_examples.rb
index 71eec9f3217..8666c19481c 100644
--- a/spec/support/protected_tags/access_control_ce_shared_examples.rb
+++ b/spec/support/protected_tags/access_control_ce_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples "protected tags > access control > CE" do
ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)|
it "allows creating protected tags that #{access_type_name} can create" do
diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/redis/redis_helpers.rb
index 0457e8487d8..7c571738a01 100644
--- a/spec/support/redis/redis_helpers.rb
+++ b/spec/support/redis/redis_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RedisHelpers
# config/README.md
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index 6aa59960092..7e47cdae866 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples "redis_shared_examples" do
include StubENV
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index b38c5dfe60b..1c9f9e5161e 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative "helpers/stub_configuration"
require_relative "helpers/stub_metrics"
require_relative "helpers/stub_object_storage"
diff --git a/spec/support/seed.rb b/spec/support/seed.rb
index bea2e9c3044..36cb819763b 100644
--- a/spec/support/seed.rb
+++ b/spec/support/seed.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.configure do |config|
config.include SeedHelper, :seed_helper
diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb
index b0bf942aa09..6ec8750ce87 100644
--- a/spec/support/services/clusters/create_service_shared.rb
+++ b/spec/support/services/clusters/create_service_shared.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'valid cluster create params' do
let(:params) do
{
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 8b4cffaac19..4c3644e6724 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specifications for behavior common to all objects with executable attributes.
# It can take a `default_params`.
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb
index ffbce6c42bf..5e5acd0e40a 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/services/issuable_update_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuable update service' do
def update_issuable(opts)
described_class.new(project, user, opts).execute(open_issuable)
diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
index 1284415da1f..65236f13e27 100644
--- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
+++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
shared_examples "migrating a deleted user's associated records to the ghost user" do |record_class, fields|
diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb
index 1d2a4856724..bbe442f07b0 100644
--- a/spec/support/setup_builds_storage.rb
+++ b/spec/support/setup_builds_storage.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.configure do |config|
def builds_path
Rails.root.join('tmp/tests/builds')
diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb
index 4f5d53f9317..b4d061a8215 100644
--- a/spec/support/shared_contexts/email_shared_context.rb
+++ b/spec/support/shared_contexts/email_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context :email_shared_context do
let(:mail_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
let(:receiver) { Gitlab::Email::Receiver.new(email_raw) }
diff --git a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
index a0d994c4d8d..38f6011646e 100644
--- a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.shared_context 'GroupProjectsFinder context' do
diff --git a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
index b8a9554f55f..26ab6fbd400 100644
--- a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.shared_context 'IssuesFinder context' do
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
index ab6687f1d07..ef1e65d2577 100644
--- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests context' do
diff --git a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb
index 9e1f89ee0ed..d6404b2ee4b 100644
--- a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.shared_context 'UsersFinder#execute filter by project context' do
diff --git a/spec/support/shared_contexts/json_response_shared_context.rb b/spec/support/shared_contexts/json_response_shared_context.rb
index df5fc288089..bd37c97ed35 100644
--- a/spec/support/shared_contexts/json_response_shared_context.rb
+++ b/spec/support/shared_contexts/json_response_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'JSON response' do
let(:json_response) { JSON.parse(response.body) }
end
diff --git a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
index 05424d08b9d..276ebf973c8 100644
--- a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
+++ b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'merge request allowing collaboration' do
include ProjectForksHelper
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 74389c4d82b..c11725c63d2 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -31,7 +31,7 @@ RSpec.shared_context 'GroupPolicy context' do
:admin_group_member,
:change_visibility_level,
:set_note_created_at,
- (Gitlab::Database.postgresql? ? :create_subgroup : nil)
+ :create_subgroup
].compact
end
diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb
index 0c3a24d206f..4d176ab5fca 100644
--- a/spec/support/shared_contexts/services_shared_context.rb
+++ b/spec/support/shared_contexts/services_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Service.available_services_names.each do |service|
shared_context service do
let(:dashed_service) { service.dasherize }
diff --git a/spec/support/shared_contexts/url_shared_context.rb b/spec/support/shared_contexts/url_shared_context.rb
index 1b1f67daac3..560cd500ecd 100644
--- a/spec/support/shared_contexts/url_shared_context.rb
+++ b/spec/support/shared_contexts/url_shared_context.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'invalid urls' do
let(:urls_with_CRLF) do
["http://127.0.0.1:333/pa\rth",
diff --git a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
index dc97a39f051..82975027e5b 100644
--- a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'chat slash commands service' do
describe "Associations" do
it { is_expected.to respond_to :token }
diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb
index 68c2b6e10e2..6fd4b14d51d 100644
--- a/spec/support/shared_examples/ci_trace_shared_examples.rb
+++ b/spec/support/shared_examples/ci_trace_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'common trace features' do
describe '#html' do
before do
diff --git a/spec/support/shared_examples/common_system_notes_examples.rb b/spec/support/shared_examples/common_system_notes_examples.rb
index da5a4f3e319..75f93a32d78 100644
--- a/spec/support/shared_examples/common_system_notes_examples.rb
+++ b/spec/support/shared_examples/common_system_notes_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'system note creation' do |update_params, note_text|
subject { described_class.new(project, user).execute(issuable, old_labels: []) }
diff --git a/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb b/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb
index 8dd78fd0a25..2faa0cf8c1c 100644
--- a/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples 'disabled when using an external authorization service' do
diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
index f4b02dc5350..fb22498f84f 100644
--- a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuable notes filter' do
let(:params) do
if issuable_parent.is_a?(Project)
diff --git a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
index eb051166a69..1cd14ea2251 100644
--- a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'set sort order from user preference' do
describe '#set_sort_order_from_user_preference' do
# There is no issuable_sorting_field defined in any CE controllers yet,
diff --git a/spec/support/shared_examples/controllers/todos_shared_examples.rb b/spec/support/shared_examples/controllers/todos_shared_examples.rb
index bafd9bac8d0..f3f9abb7da2 100644
--- a/spec/support/shared_examples/controllers/todos_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/todos_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'todos actions' do
context 'when authorized' do
before do
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
index 59708173716..97b2a01576c 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'handle uploads' do
let(:user) { create(:user) }
let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
diff --git a/spec/support/shared_examples/controllers/variables_shared_examples.rb b/spec/support/shared_examples/controllers/variables_shared_examples.rb
index e80722857ec..78666e677ef 100644
--- a/spec/support/shared_examples/controllers/variables_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/variables_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'GET #show lists all variables' do
it 'renders the variables as json' do
subject
diff --git a/spec/support/shared_examples/dirty_submit_form_shared_examples.rb b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb
index 4e45e2921e7..60c8899d349 100644
--- a/spec/support/shared_examples/dirty_submit_form_shared_examples.rb
+++ b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'dirty submit form' do |selector_args|
selectors = selector_args.is_a?(Array) ? selector_args : [selector_args]
diff --git a/spec/support/shared_examples/email_format_shared_examples.rb b/spec/support/shared_examples/email_format_shared_examples.rb
index b924a208e71..22d6c2b38e3 100644
--- a/spec/support/shared_examples/email_format_shared_examples.rb
+++ b/spec/support/shared_examples/email_format_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specifications for behavior common to all objects with an email attribute.
# Takes a list of email-format attributes and requires:
# - subject { "the object with a attribute= setter" }
diff --git a/spec/support/shared_examples/fast_destroy_all.rb b/spec/support/shared_examples/fast_destroy_all.rb
index a8079b6d864..a64259c03f2 100644
--- a/spec/support/shared_examples/fast_destroy_all.rb
+++ b/spec/support/shared_examples/fast_destroy_all.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'fast destroyable' do
describe 'Forbid #destroy and #destroy_all' do
it 'does not delete database rows and associted external data' do
diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
index 2b36955a3c4..f24e47f4638 100644
--- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
+++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'comment on merge request file' do
it 'adds a comment' do
click_diff_line(find("[id='#{sample_commit.line_code}']"))
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index ec1b1754cf0..c0db4cdde72 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'a creatable merge request' do
include WaitForRequests
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index a6121fcc50a..964c80007b0 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'an editable merge request' do
it 'updates merge request', :js do
find('.js-assignee-search').click
diff --git a/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb
index 96c821b26f7..09a48533ee3 100644
--- a/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issue sidebar stays collapsed on mobile' do
before do
resize_screen_xs
diff --git a/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb b/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb
index c92c7f603d6..63ed37cde03 100644
--- a/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuable user dropdown behaviors' do
include FilteredSearchHelpers
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
index d87e5fcaa88..8e1d24c4be2 100644
--- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'Maintainer manages access requests' do
let(:user) { create(:user) }
let(:maintainer) { create(:user) }
diff --git a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb
index 64c3b80136d..51559c0b110 100644
--- a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'project features apply to issuables' do |klass|
let(:described_class) { klass }
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
index a8f2c2e7a5a..867a1774aa9 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples "protected branches > access control > CE" do
ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)|
it "allows creating protected branches that #{access_type_name} can push to" do
diff --git a/spec/support/shared_examples/features/search_shared_examples.rb b/spec/support/shared_examples/features/search_shared_examples.rb
index 25ebbf011d5..e27d6700cbf 100644
--- a/spec/support/shared_examples/features/search_shared_examples.rb
+++ b/spec/support/shared_examples/features/search_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'top right search form' do
it 'does not show top right search form' do
expect(page).not_to have_selector('.search')
diff --git a/spec/support/shared_examples/file_finder.rb b/spec/support/shared_examples/file_finder.rb
index 0dc351b5149..984a06ccd1a 100644
--- a/spec/support/shared_examples/file_finder.rb
+++ b/spec/support/shared_examples/file_finder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'file finder' do
let(:query) { 'files' }
let(:search_results) { subject.find(query) }
diff --git a/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb b/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb
index d7e17cc0b70..b8b0079e36d 100644
--- a/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb
+++ b/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples 'a finder with external authorization service' do
diff --git a/spec/support/shared_examples/gitlab_verify.rb b/spec/support/shared_examples/gitlab_verify.rb
index 560913ca92f..721ea3b4c88 100644
--- a/spec/support/shared_examples/gitlab_verify.rb
+++ b/spec/support/shared_examples/gitlab_verify.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'Gitlab::Verify::BatchVerifier subclass' do
describe 'batching' do
let(:first_batch) { objects[0].id..objects[0].id }
diff --git a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
index 713f0a879c1..145c476c7f7 100644
--- a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'issuable state' do
it 'exposes all the existing issuable states' do
expect(described_class.values.keys).to include(*%w[opened closed locked])
diff --git a/spec/support/shared_examples/group_members_shared_example.rb b/spec/support/shared_examples/group_members_shared_example.rb
index 547c83c7955..4f7d496741d 100644
--- a/spec/support/shared_examples/group_members_shared_example.rb
+++ b/spec/support/shared_examples/group_members_shared_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'members and requesters associations' do
describe '#members_and_requesters' do
it 'includes members and requesters' do
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
index 01bee603274..17f495ebe46 100644
--- a/spec/support/shared_examples/helm_generated_script.rb
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'helm commands' do
describe '#generate_script' do
let(:helm_setup) do
diff --git a/spec/support/shared_examples/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb
index d97b21f71cd..3460a8ba297 100644
--- a/spec/support/shared_examples/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/issuable_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'cache counters invalidator' do
it 'invalidates counter cache for assignees' do
expect_any_instance_of(User).to receive(:invalidate_merge_request_cache_counts)
diff --git a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb
index 244f4766a84..52d90b5f183 100644
--- a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb
+++ b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuables list meta-data' do |issuable_type, action = nil|
include ProjectForksHelper
diff --git a/spec/support/shared_examples/issue_tracker_service_shared_example.rb b/spec/support/shared_examples/issue_tracker_service_shared_example.rb
index a6ab03cb808..0a483fd30ba 100644
--- a/spec/support/shared_examples/issue_tracker_service_shared_example.rb
+++ b/spec/support/shared_examples/issue_tracker_service_shared_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr|
it { is_expected.to allow_value('https://example.com').for(url_attr) }
diff --git a/spec/support/shared_examples/ldap_shared_examples.rb b/spec/support/shared_examples/ldap_shared_examples.rb
index 52c34e78965..0a70ce7ea0c 100644
--- a/spec/support/shared_examples/ldap_shared_examples.rb
+++ b/spec/support/shared_examples/ldap_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'normalizes a DN' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb
index f326e502092..22e5698825d 100644
--- a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb
+++ b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'redirecting a legacy path' do |source, target|
include RSpec::Rails::RequestExampleGroup
diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb
index dcf7c1a90c2..2cbc0c2bdf2 100644
--- a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'backfill migration for project repositories' do |storage|
describe '#perform' do
let(:storage_versions) { storage == :legacy ? [nil, 0] : [1, 2] }
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/a_redis_counter.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/a_redis_counter.rb
new file mode 100644
index 00000000000..91bf804978d
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/a_redis_counter.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+shared_examples 'a redis usage counter' do |thing, event|
+ describe ".count(#{event})", :clean_gitlab_redis_shared_state do
+ it "increments the #{thing} #{event} counter by 1" do
+ expect do
+ described_class.count(event)
+ end.to change { described_class.read(event) }.by 1
+ end
+ end
+
+ describe ".read(#{event})", :clean_gitlab_redis_shared_state do
+ event_count = 5
+
+ it "returns the total number of #{event} events" do
+ event_count.times do
+ described_class.count(event)
+ end
+
+ expect(described_class.read(event)).to eq(event_count)
+ end
+ end
+end
+
+shared_examples 'a redis usage counter with totals' do |prefix, events|
+ describe 'totals', :clean_gitlab_redis_shared_state do
+ before do
+ events.each do |k, n|
+ n.times do
+ described_class.count(k)
+ end
+ end
+ end
+
+ let(:expected_totals) do
+ events.transform_keys { |k| "#{prefix}_#{k}".to_sym }
+ end
+
+ it 'can report all totals' do
+ expect(described_class.totals).to include(expected_totals)
+ end
+ end
+
+ # Override these let-bindings to adjust the unknown events tests
+ let(:unknown_event) { described_class::UnknownEvent }
+ let(:bad_event) { :wibble }
+
+ describe 'unknown events' do
+ it 'cannot increment' do
+ expect { described_class.count(bad_event) }.to raise_error unknown_event
+ end
+
+ it 'cannot read' do
+ expect { described_class.read(bad_event) }.to raise_error unknown_event
+ end
+ end
+end
diff --git a/spec/support/shared_examples/malicious_regexp_shared_examples.rb b/spec/support/shared_examples/malicious_regexp_shared_examples.rb
index a86050e2cf2..96c02260d53 100644
--- a/spec/support/shared_examples/malicious_regexp_shared_examples.rb
+++ b/spec/support/shared_examples/malicious_regexp_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'timeout'
shared_examples 'malicious regexp' do
diff --git a/spec/support/shared_examples/mentionable_shared_examples.rb b/spec/support/shared_examples/mentionable_shared_examples.rb
index fea52c2eeb2..93a8c4709a6 100644
--- a/spec/support/shared_examples/mentionable_shared_examples.rb
+++ b/spec/support/shared_examples/mentionable_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specifications for behavior common to all Mentionable implementations.
# Requires a shared context containing:
# - subject { "the mentionable implementation" }
diff --git a/spec/support/shared_examples/milestone_tabs_examples.rb b/spec/support/shared_examples/milestone_tabs_examples.rb
index 8b757586941..bda4b978737 100644
--- a/spec/support/shared_examples/milestone_tabs_examples.rb
+++ b/spec/support/shared_examples/milestone_tabs_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'milestone tabs' do
def go(path, extra_params = {})
params =
diff --git a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
index a248f60d23e..b837ca87256 100644
--- a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
+++ b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples_for 'AtomicInternalId' do |validate_presence: true|
diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb
index 8d70c06c468..b6a3d50d14a 100644
--- a/spec/support/shared_examples/models/chat_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
shared_examples_for "chat service" do |service_name|
diff --git a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
index d6490a808ce..8e58cc7ba22 100644
--- a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'cluster application core specs' do |application_name|
it { is_expected.to belong_to(:cluster) }
it { is_expected.to validate_presence_of(:cluster) }
diff --git a/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb
index bd3661471f8..7ddb3b11c85 100644
--- a/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'cluster application helm specs' do |application_name|
let(:application) { create(application_name) }
diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
index 4525c03837f..5341aacb445 100644
--- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'cluster application status specs' do |application_name|
describe '#status' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
diff --git a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
index a4762b68858..7ea2bb265cc 100644
--- a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
+++ b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This shared example requires a `builder` and `user` variable
shared_examples 'issuable hook data' do |kind|
let(:data) { builder.build(user: user) }
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
index ef5cea3f2a5..050d710f1de 100644
--- a/spec/support/shared_examples/models/members_notifications_shared_example.rb
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'members notifications' do |entity_type|
let(:notification_service) { double('NotificationService').as_null_object }
diff --git a/spec/support/shared_examples/models/project_hook_data_shared_examples.rb b/spec/support/shared_examples/models/project_hook_data_shared_examples.rb
index f0264878811..03d10c10e3c 100644
--- a/spec/support/shared_examples/models/project_hook_data_shared_examples.rb
+++ b/spec/support/shared_examples/models/project_hook_data_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'project hook data with deprecateds' do |project_key: :project|
it 'contains project data' do
expect(data[project_key][:name]).to eq(project.name)
diff --git a/spec/support/shared_examples/models/with_uploads_shared_examples.rb b/spec/support/shared_examples/models/with_uploads_shared_examples.rb
index 43033a2d256..eb1ade03017 100644
--- a/spec/support/shared_examples/models/with_uploads_shared_examples.rb
+++ b/spec/support/shared_examples/models/with_uploads_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples_for 'model with uploads' do |supports_fileuploads|
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index 4452b1c82cb..ca031df000e 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'gitlab email notification' do
set(:group) { create(:group) }
set(:subgroup) { create(:group, parent: group) }
@@ -50,7 +52,7 @@ shared_examples 'an email sent to a user' do
it 'is sent to user\'s group notification email' do
group_notification_email = 'user+group@example.com'
- create(:notification_setting, user: recipient, source: project.group, notification_email: group_notification_email)
+ create(:notification_setting, user: recipient, source: group, notification_email: group_notification_email)
expect(subject).to deliver_to(group_notification_email)
end
diff --git a/spec/support/shared_examples/position_formatters.rb b/spec/support/shared_examples/position_formatters.rb
index ffc9456dbc7..30b6b8d24f0 100644
--- a/spec/support/shared_examples/position_formatters.rb
+++ b/spec/support/shared_examples/position_formatters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for "position formatter" do
let(:formatter) { described_class.new(attrs) }
diff --git a/spec/support/shared_examples/project_latest_successful_build_for_examples.rb b/spec/support/shared_examples/project_latest_successful_build_for_examples.rb
new file mode 100644
index 00000000000..a9bd23e9fc9
--- /dev/null
+++ b/spec/support/shared_examples/project_latest_successful_build_for_examples.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+shared_examples 'latest successful build for sha or ref' do
+ context 'with many builds' do
+ let(:other_pipeline) { create_pipeline(project) }
+ let(:other_build) { create_build(other_pipeline, 'test') }
+ let(:build_name) { other_build.name }
+
+ before do
+ pipeline1 = create_pipeline(project)
+ pipeline2 = create_pipeline(project)
+ create_build(pipeline1, 'test')
+ create_build(pipeline1, 'test2')
+ create_build(pipeline2, 'test2')
+ end
+
+ it 'gives the latest builds from latest pipeline' do
+ expect(subject).to eq(other_build)
+ end
+ end
+
+ context 'with succeeded pipeline' do
+ let!(:build) { create_build }
+ let(:build_name) { build.name }
+
+ context 'standalone pipeline' do
+ it 'returns builds for ref for default_branch' do
+ expect(subject).to eq(build)
+ end
+
+ context 'with nonexistent build' do
+ let(:build_name) { 'TAIL' }
+
+ it 'returns empty relation if the build cannot be found' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ context 'with some pending pipeline' do
+ before do
+ create_build(create_pipeline(project, 'pending'))
+ end
+
+ it 'gives the latest build from latest pipeline' do
+ expect(subject).to eq(build)
+ end
+ end
+ end
+
+ context 'with pending pipeline' do
+ let!(:pending_build) { create_build(pipeline) }
+ let(:build_name) { pending_build.name }
+
+ before do
+ pipeline.update(status: 'pending')
+ end
+
+ it 'returns empty relation' do
+ expect(subject).to be_nil
+ end
+ end
+end
diff --git a/spec/support/shared_examples/reference_parser_shared_examples.rb b/spec/support/shared_examples/reference_parser_shared_examples.rb
index baf8bcc04b8..d903c0f10e0 100644
--- a/spec/support/shared_examples/reference_parser_shared_examples.rb
+++ b/spec/support/shared_examples/reference_parser_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples "referenced feature visibility" do |*related_features|
let(:feature_fields) do
related_features.map { |feature| (feature + "_access_level").to_sym }
diff --git a/spec/support/shared_examples/relative_positioning_shared_examples.rb b/spec/support/shared_examples/relative_positioning_shared_examples.rb
new file mode 100644
index 00000000000..5ee62644c54
--- /dev/null
+++ b/spec/support/shared_examples/relative_positioning_shared_examples.rb
@@ -0,0 +1,253 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples "a class that supports relative positioning" do
+ let(:item1) { create(factory, default_params) }
+ let(:item2) { create(factory, default_params) }
+ let(:new_item) { create(factory, default_params) }
+
+ def create_item(params)
+ create(factory, params.merge(default_params))
+ end
+
+ describe '.move_to_end' do
+ it 'moves the object to the end' do
+ item1.update(relative_position: 5)
+ item2.update(relative_position: 15)
+
+ described_class.move_to_end([item1, item2])
+
+ expect(item2.prev_relative_position).to eq item1.relative_position
+ expect(item1.prev_relative_position).to eq nil
+ expect(item2.next_relative_position).to eq nil
+ end
+
+ it 'does not perform any moves if all items have their relative_position set' do
+ item1.update!(relative_position: 1)
+
+ expect(item1).not_to receive(:save)
+
+ described_class.move_to_end([item1])
+ end
+ end
+
+ describe '#max_relative_position' do
+ it 'returns maximum position' do
+ expect(item1.max_relative_position).to eq item2.relative_position
+ end
+ end
+
+ describe '#prev_relative_position' do
+ it 'returns previous position if there is an item above' do
+ item1.update(relative_position: 5)
+ item2.update(relative_position: 15)
+
+ expect(item2.prev_relative_position).to eq item1.relative_position
+ end
+
+ it 'returns nil if there is no item above' do
+ expect(item1.prev_relative_position).to eq nil
+ end
+ end
+
+ describe '#next_relative_position' do
+ it 'returns next position if there is an item below' do
+ item1.update(relative_position: 5)
+ item2.update(relative_position: 15)
+
+ expect(item1.next_relative_position).to eq item2.relative_position
+ end
+
+ it 'returns nil if there is no item below' do
+ expect(item2.next_relative_position).to eq nil
+ end
+ end
+
+ describe '#move_before' do
+ it 'moves item before' do
+ [item2, item1].each(&:move_to_end)
+
+ item1.move_before(item2)
+
+ expect(item1.relative_position).to be < item2.relative_position
+ end
+ end
+
+ describe '#move_after' do
+ it 'moves item after' do
+ [item1, item2].each(&:move_to_end)
+
+ item1.move_after(item2)
+
+ expect(item1.relative_position).to be > item2.relative_position
+ end
+ end
+
+ describe '#move_to_end' do
+ before do
+ [item1, item2].each do |item1|
+ item1.move_to_end && item1.save
+ end
+ end
+
+ it 'moves item to the end' do
+ new_item.move_to_end
+
+ expect(new_item.relative_position).to be > item2.relative_position
+ end
+ end
+
+ describe '#shift_after?' do
+ before do
+ [item1, item2].each do |item1|
+ item1.move_to_end && item1.save
+ end
+ end
+
+ it 'returns true' do
+ item1.update(relative_position: item2.relative_position - 1)
+
+ expect(item1.shift_after?).to be_truthy
+ end
+
+ it 'returns false' do
+ item1.update(relative_position: item2.relative_position - 2)
+
+ expect(item1.shift_after?).to be_falsey
+ end
+ end
+
+ describe '#shift_before?' do
+ before do
+ [item1, item2].each do |item1|
+ item1.move_to_end && item1.save
+ end
+ end
+
+ it 'returns true' do
+ item1.update(relative_position: item2.relative_position + 1)
+
+ expect(item1.shift_before?).to be_truthy
+ end
+
+ it 'returns false' do
+ item1.update(relative_position: item2.relative_position + 2)
+
+ expect(item1.shift_before?).to be_falsey
+ end
+ end
+
+ describe '#move_between' do
+ before do
+ [item1, item2].each do |item1|
+ item1.move_to_end && item1.save
+ end
+ end
+
+ it 'positions item between two other' do
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to be > item1.relative_position
+ expect(new_item.relative_position).to be < item2.relative_position
+ end
+
+ it 'positions item between on top' do
+ new_item.move_between(nil, item1)
+
+ expect(new_item.relative_position).to be < item1.relative_position
+ end
+
+ it 'positions item between to end' do
+ new_item.move_between(item2, nil)
+
+ expect(new_item.relative_position).to be > item2.relative_position
+ end
+
+ it 'positions items even when after and before positions are the same' do
+ item2.update relative_position: item1.relative_position
+
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to be > item1.relative_position
+ expect(item1.relative_position).to be < item2.relative_position
+ end
+
+ it 'positions items between other two if distance is 1' do
+ item2.update relative_position: item1.relative_position + 1
+
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to be > item1.relative_position
+ expect(item1.relative_position).to be < item2.relative_position
+ end
+
+ it 'positions item in the middle of other two if distance is big enough' do
+ item1.update relative_position: 6000
+ item2.update relative_position: 10000
+
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to eq(8000)
+ end
+
+ it 'positions item closer to the middle if we are at the very top' do
+ item2.update relative_position: 6000
+
+ new_item.move_between(nil, item2)
+
+ expect(new_item.relative_position).to eq(6000 - RelativePositioning::IDEAL_DISTANCE)
+ end
+
+ it 'positions item closer to the middle if we are at the very bottom' do
+ new_item.update relative_position: 1
+ item1.update relative_position: 6000
+ item2.destroy
+
+ new_item.move_between(item1, nil)
+
+ expect(new_item.relative_position).to eq(6000 + RelativePositioning::IDEAL_DISTANCE)
+ end
+
+ it 'positions item in the middle of other two if distance is not big enough' do
+ item1.update relative_position: 100
+ item2.update relative_position: 400
+
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to eq(250)
+ end
+
+ it 'positions item in the middle of other two is there is no place' do
+ item1.update relative_position: 100
+ item2.update relative_position: 101
+
+ new_item.move_between(item1, item2)
+
+ expect(new_item.relative_position).to be_between(item1.relative_position, item2.relative_position)
+ end
+
+ it 'uses rebalancing if there is no place' do
+ item1.update relative_position: 100
+ item2.update relative_position: 101
+ item3 = create_item(relative_position: 102)
+ new_item.update relative_position: 103
+
+ new_item.move_between(item2, item3)
+ new_item.save!
+
+ expect(new_item.relative_position).to be_between(item2.relative_position, item3.relative_position)
+ expect(item1.reload.relative_position).not_to eq(100)
+ end
+
+ it 'positions item right if we pass none-sequential parameters' do
+ item1.update relative_position: 99
+ item2.update relative_position: 101
+ item3 = create_item(relative_position: 102)
+ new_item.update relative_position: 103
+
+ new_item.move_between(item1, item3)
+ new_item.save!
+
+ expect(new_item.relative_position).to be(100)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
index 8a7fcf856a1..776a0bdd29e 100644
--- a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'custom attributes endpoints' do |attributable_name|
let!(:custom_attribute1) { attributable.custom_attributes.create key: 'foo', value: 'foo' }
let!(:custom_attribute2) { attributable.custom_attributes.create key: 'bar', value: 'bar' }
diff --git a/spec/support/shared_examples/requests/api/diff_discussions.rb b/spec/support/shared_examples/requests/api/diff_discussions.rb
index 366c2955359..76c6c93964a 100644
--- a/spec/support/shared_examples/requests/api/diff_discussions.rb
+++ b/spec/support/shared_examples/requests/api/diff_discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'diff discussions API' do |parent_type, noteable_type, id_name|
describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
it "includes diff discussions" do
diff --git a/spec/support/shared_examples/requests/api/discussions.rb b/spec/support/shared_examples/requests/api/discussions.rb
index c3132c41f5b..fc72287f265 100644
--- a/spec/support/shared_examples/requests/api/discussions.rb
+++ b/spec/support/shared_examples/requests/api/discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'discussions API' do |parent_type, noteable_type, id_name, can_reply_to_individual_notes: false|
describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
it "returns an array of discussions" do
diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
index 96d59e0c472..9fe6288d53f 100644
--- a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issuable participants endpoint' do
let(:area) { entity.class.name.underscore.pluralize }
diff --git a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
index 5f4e178f2e5..90c1ed8d09b 100644
--- a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
def get_issue
json_response.is_a?(Array) ? json_response.detect {|issue| issue['id'] == target_issue.id} : json_response
end
diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb
index 57eefd5ef01..354ae7288b1 100644
--- a/spec/support/shared_examples/requests/api/notes.rb
+++ b/spec/support/shared_examples/requests/api/notes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
context 'sorting' do
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions.rb b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
index 7e2416b23f3..42054a273f3 100644
--- a/spec/support/shared_examples/requests/api/resolvable_discussions.rb
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'resolvable discussions API' do |parent_type, noteable_type, id_name|
describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
it "resolves discussion if resolved is true" do
diff --git a/spec/support/shared_examples/requests/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb
index ebfc5fed3bb..eebed7e42c1 100644
--- a/spec/support/shared_examples/requests/api/status_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specs for status checking.
#
# Requires an API request:
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index 04140cad3f0..2a38d56141a 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
shared_examples 'a working graphql query' do
diff --git a/spec/support/shared_examples/resource_label_events_api.rb b/spec/support/shared_examples/resource_label_events_api.rb
new file mode 100644
index 00000000000..945cb8d9f2c
--- /dev/null
+++ b/spec/support/shared_examples/resource_label_events_api.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+shared_examples 'resource_label_events API' do |parent_type, eventable_type, id_name|
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events" do
+ it "returns an array of resource label events" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(event.id)
+ end
+
+ it "returns a 404 error when eventable id not found" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/12345/resource_label_events", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it "returns 404 when not authorized" do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ private_user = create(:user)
+
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events", private_user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_label_events/:event_id" do
+ it "returns a resource label event by id" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/#{event.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(event.id)
+ end
+
+ it "returns a 404 error if resource label event not found" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_label_events/12345", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/serializers/note_entity_examples.rb b/spec/support/shared_examples/serializers/note_entity_examples.rb
index ec208aba2a9..bfcaa2f1bd5 100644
--- a/spec/support/shared_examples/serializers/note_entity_examples.rb
+++ b/spec/support/shared_examples/serializers/note_entity_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'note entity' do
subject { entity.as_json }
diff --git a/spec/support/shared_examples/services/boards/boards_create_service.rb b/spec/support/shared_examples/services/boards/boards_create_service.rb
index 5bdc04f660f..19818a6091b 100644
--- a/spec/support/shared_examples/services/boards/boards_create_service.rb
+++ b/spec/support/shared_examples/services/boards/boards_create_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'boards create service' do
context 'when parent does not have a board' do
it 'creates a new board' do
diff --git a/spec/support/shared_examples/services/boards/boards_list_service.rb b/spec/support/shared_examples/services/boards/boards_list_service.rb
index e0d5a7c61f2..566e5050f8e 100644
--- a/spec/support/shared_examples/services/boards/boards_list_service.rb
+++ b/spec/support/shared_examples/services/boards/boards_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'boards list service' do
context 'when parent does not have a board' do
it 'creates a new parent board' do
diff --git a/spec/support/shared_examples/services/boards/issues_list_service.rb b/spec/support/shared_examples/services/boards/issues_list_service.rb
index 8b879cef084..75733c774ef 100644
--- a/spec/support/shared_examples/services/boards/issues_list_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issues list service' do
it 'delegates search to IssuesFinder' do
params = { board_id: board.id, id: list1.id }
diff --git a/spec/support/shared_examples/services/boards/issues_move_service.rb b/spec/support/shared_examples/services/boards/issues_move_service.rb
index 5359831f8f8..d3fa8084185 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'issues move service' do |group|
shared_examples 'updating timestamps' do
it 'updates updated_at' do
diff --git a/spec/support/shared_examples/services/boards/lists_destroy_service.rb b/spec/support/shared_examples/services/boards/lists_destroy_service.rb
index 62b6ffe1836..95725078f9d 100644
--- a/spec/support/shared_examples/services/boards/lists_destroy_service.rb
+++ b/spec/support/shared_examples/services/boards/lists_destroy_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'lists destroy service' do
context 'when list type is label' do
it 'removes list from board' do
diff --git a/spec/support/shared_examples/services/boards/lists_list_service.rb b/spec/support/shared_examples/services/boards/lists_list_service.rb
index 0a8220111ab..29784f6da08 100644
--- a/spec/support/shared_examples/services/boards/lists_list_service.rb
+++ b/spec/support/shared_examples/services/boards/lists_list_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'lists list service' do
context 'when the board has a backlog list' do
let!(:backlog_list) { create(:backlog_list, board: board) }
diff --git a/spec/support/shared_examples/services/boards/lists_move_service.rb b/spec/support/shared_examples/services/boards/lists_move_service.rb
index 2cdb968a45d..0b3bfd8e2a8 100644
--- a/spec/support/shared_examples/services/boards/lists_move_service.rb
+++ b/spec/support/shared_examples/services/boards/lists_move_service.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'lists move service' do
let!(:planning) { create(:list, board: board, position: 0) }
let!(:development) { create(:list, board: board, position: 1) }
diff --git a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
index 02de47a96dd..1e0ac8b7615 100644
--- a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'check ingress ip executions' do |app_name|
describe '#execute' do
let(:application) { create(app_name, :installed) }
diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
index b8db35a6ef9..1c3fa5644d3 100644
--- a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'gitlab projects import validations' do
context 'with an invalid path' do
let(:path) { '/invalid-path/' }
diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index bbed3b1f6a9..8ce94064dc3 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f }
RSpec.shared_examples 'slack or mattermost notifications' do
diff --git a/spec/support/shared_examples/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
index 833c31a57cb..b5321c6db34 100644
--- a/spec/support/shared_examples/snippet_visibility_shared_examples.rb
+++ b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'snippet visibility' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/support/shared_examples/snippets_shared_examples.rb b/spec/support/shared_examples/snippets_shared_examples.rb
index 85f0facd5c3..5c35617bd36 100644
--- a/spec/support/shared_examples/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/snippets_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# These shared examples expect a `snippets` array of snippets
RSpec.shared_examples 'paginated snippets' do |remote: false|
it "is limited to #{Snippet.default_per_page} items per page" do
diff --git a/spec/support/shared_examples/taskable_shared_examples.rb b/spec/support/shared_examples/taskable_shared_examples.rb
index 4a1df1ce380..f04f509f3d2 100644
--- a/spec/support/shared_examples/taskable_shared_examples.rb
+++ b/spec/support/shared_examples/taskable_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# Specs for task state functionality for issues and merge requests.
#
# Requires a context containing:
diff --git a/spec/support/shared_examples/throttled_touch.rb b/spec/support/shared_examples/throttled_touch.rb
index eba990d4037..aaaa590862d 100644
--- a/spec/support/shared_examples/throttled_touch.rb
+++ b/spec/support/shared_examples/throttled_touch.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples_for 'throttled touch' do
describe '#touch' do
it 'updates the updated_at timestamp' do
diff --git a/spec/support/shared_examples/unique_ip_check_shared_examples.rb b/spec/support/shared_examples/unique_ip_check_shared_examples.rb
index e5c8ac6a004..65d86ddee9e 100644
--- a/spec/support/shared_examples/unique_ip_check_shared_examples.rb
+++ b/spec/support/shared_examples/unique_ip_check_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'unique ips sign in limit' do
include StubENV
before do
diff --git a/spec/support/shared_examples/update_invalid_issuable.rb b/spec/support/shared_examples/update_invalid_issuable.rb
index 4cb6d001b9b..b7ac08372f9 100644
--- a/spec/support/shared_examples/update_invalid_issuable.rb
+++ b/spec/support/shared_examples/update_invalid_issuable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples 'update invalid issuable' do |klass|
let(:params) do
{
diff --git a/spec/support/shared_examples/updating_mentions_shared_examples.rb b/spec/support/shared_examples/updating_mentions_shared_examples.rb
index 5e3f19ba19e..ef385f94cc2 100644
--- a/spec/support/shared_examples/updating_mentions_shared_examples.rb
+++ b/spec/support/shared_examples/updating_mentions_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'updating mentions' do |service_class|
let(:mentioned_user) { create(:user) }
let(:service_class) { service_class }
diff --git a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
index 1190863d88e..9263aaff89a 100644
--- a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
+++ b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_examples "matches the method pattern" do |method|
let(:target) { subject }
let(:args) { nil }
diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
index 1bd176280c5..5d605dd811b 100644
--- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
+++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
shared_context 'with storage' do |store, **stub_params|
before do
subject.object_store = store
diff --git a/spec/support/shared_examples/url_validator_examples.rb b/spec/support/shared_examples/url_validator_examples.rb
index 25277ccd9aa..16fceddb605 100644
--- a/spec/support/shared_examples/url_validator_examples.rb
+++ b/spec/support/shared_examples/url_validator_examples.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.shared_examples 'url validator examples' do |schemes|
let(:validator) { described_class.new(attributes: [:link_url], **options) }
let!(:badge) { build(:badge, link_url: 'http://www.example.com') }
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
index d1a765f27b9..585c458a64e 100644
--- a/spec/support/sidekiq.rb
+++ b/spec/support/sidekiq.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'sidekiq/testing/inline'
# If Sidekiq::Testing.inline! is used, SQL transactions done inside
diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb
index 55212355daa..95f0f971787 100644
--- a/spec/support/stored_repositories.rb
+++ b/spec/support/stored_repositories.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
RSpec.configure do |config|
config.before(:each, :broken_storage) do
allow(Gitlab::GitalyClient).to receive(:call) do
diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb
index 6840fb9a860..6ba50c83b25 100644
--- a/spec/support/test_reports/test_reports_helper.rb
+++ b/spec/support/test_reports/test_reports_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TestReportsHelper
def create_test_case_rspec_success(name = 'test_spec')
Gitlab::Ci::Reports::TestCase.new(
diff --git a/spec/support/trace/trace_helpers.rb b/spec/support/trace/trace_helpers.rb
index c7802bbcb94..9255715ff71 100644
--- a/spec/support/trace/trace_helpers.rb
+++ b/spec/support/trace/trace_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module TraceHelpers
def create_legacy_trace(build, content)
File.open(legacy_trace_path(build), 'wb') { |stream| stream.write(content) }
diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb
index 9ac7e7fc515..32b88edc2df 100644
--- a/spec/support/webmock.rb
+++ b/spec/support/webmock.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'webmock'
require 'webmock/rspec'
diff --git a/spec/views/groups/edit.html.haml_spec.rb b/spec/views/groups/edit.html.haml_spec.rb
index 29e15960fb8..47804411b9d 100644
--- a/spec/views/groups/edit.html.haml_spec.rb
+++ b/spec/views/groups/edit.html.haml_spec.rb
@@ -35,7 +35,7 @@ describe 'groups/edit.html.haml' do
it_behaves_like '"Share with group lock" setting', { disabled: false, checked: false }
end
- context 'for a subgroup', :nested_groups do
+ context 'for a subgroup' do
let!(:subgroup) { create(:group, parent: root_group) }
let(:sub_owner) { create(:user) }
let(:test_group) { subgroup }
diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
index 2e19d0cec26..26e429ac5d0 100644
--- a/spec/views/layouts/header/_new_dropdown.haml_spec.rb
+++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
@@ -28,7 +28,7 @@ describe 'layouts/header/_new_dropdown' do
)
end
- it 'has a "New subgroup" link', :nested_groups do
+ it 'has a "New subgroup" link' do
render
expect(rendered).to have_link(
diff --git a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
index 0206928a211..88c4b52b3a6 100644
--- a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb
@@ -12,6 +12,7 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
assign(:hidden_commit_count, 0)
assign(:total_commit_count, merge_request.commits.count)
assign(:project, merge_request.target_project)
+ assign(:target_project, merge_request.target_project)
assign(:mr_presenter, merge_request.present(current_user: merge_request.author))
allow(view).to receive(:can?).and_return(true)
diff --git a/spec/views/projects/merge_requests/edit.html.haml_spec.rb b/spec/views/projects/merge_requests/edit.html.haml_spec.rb
index 529afa03f9c..0a3a46210ed 100644
--- a/spec/views/projects/merge_requests/edit.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/edit.html.haml_spec.rb
@@ -23,6 +23,7 @@ describe 'projects/merge_requests/edit.html.haml' do
before do
assign(:project, project)
+ assign(:target_project, project)
assign(:merge_request, closed_merge_request)
assign(:mr_presenter, closed_merge_request.present(current_user: user))
diff --git a/yarn.lock b/yarn.lock
index 4fa7665b000..7095ff706c2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -996,10 +996,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579"
integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA==
-"@gitlab/ui@^5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.7.1.tgz#e55d04052dd6e50ed1e90676aacc64290d62c0b6"
- integrity sha512-F06/6z6/69LbKIK0PYRDTB/teSPUnF7LijHl4JiuYHXn7Y2/iVoLsAMikhT89RVR84orHPGnw16vtCPjSjBDrA==
+"@gitlab/ui@^5.9.0":
+ version "5.9.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.9.0.tgz#a38b1b57c365608b100b95969ae7a57ce1707542"
+ integrity sha512-cgvEPWVerYZNLqkHjg5dd0VhEDBWj8aNoISZCaGOWI9K4yVtpMPVRUv19o/xYm4vUexfFsG9vg9lBgd+4ZU6Yw==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1"
@@ -8340,7 +8340,7 @@ monaco-editor@^0.15.6:
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.15.6.tgz#d63b3b06f86f803464f003b252627c3eb4a09483"
integrity sha512-JoU9V9k6KqT9R9Tiw1RTU8ohZ+Xnf9DMg6Ktqqw5hILumwmq7xqa/KLXw513uTUsWbhtnHoSJYYR++u3pkyxJg==
-mousetrap@^1.4.6:
+mousetrap@1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a"
integrity sha1-6spy4i5W1bdpt1VYc7aIwzMuOQo=