summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Barbosa Alexandre <dbalexandre@gmail.com>2019-07-09 14:45:46 -0300
committerDouglas Barbosa Alexandre <dbalexandre@gmail.com>2019-07-09 14:45:46 -0300
commit2615265ef82e07ea36df67f6d1a11ad4a731ad63 (patch)
tree92c1d3ab69f88e1dceed11ae886e1acb6ae65f1e
parent203ef336392befd907c335a4a851ec0794d5e91a (diff)
parent48c19d9e296b2bec3663f46b8d936da2082171b1 (diff)
downloadgitlab-ce-2615265ef82e07ea36df67f6d1a11ad4a731ad63.tar.gz
Merge branch 'master' into sathieu/gitlab-ce-project_api
-rw-r--r--.eslintrc.yml1
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml2
-rw-r--r--.mdlrc4
-rw-r--r--.mdlrc.style7
-rw-r--r--.rubocop_todo.yml3
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile9
-rw-r--r--Gemfile.lock26
-rw-r--r--app/assets/images/auth_buttons/salesforce_64.pngbin0 -> 8774 bytes
-rw-r--r--app/assets/javascripts/api.js6
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue8
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js3
-rw-r--r--app/assets/javascripts/boards/index.js8
-rw-r--r--app/assets/javascripts/boards/models/issue.js6
-rw-r--r--app/assets/javascripts/boards/models/list.js8
-rw-r--r--app/assets/javascripts/boards/models/milestone.js4
-rw-r--r--app/assets/javascripts/boards/mount_multiple_boards_switcher.js2
-rw-r--r--app/assets/javascripts/boards/services/board_service.js76
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js111
-rw-r--r--app/assets/javascripts/branches/divergence_graph.js4
-rw-r--r--app/assets/javascripts/commons/index.js3
-rw-r--r--app/assets/javascripts/commons/nav/user_merge_requests.js67
-rw-r--r--app/assets/javascripts/confidential_merge_request/components/dropdown.vue58
-rw-r--r--app/assets/javascripts/confidential_merge_request/components/project_form_group.vue132
-rw-r--r--app/assets/javascripts/confidential_merge_request/index.js30
-rw-r--r--app/assets/javascripts/confidential_merge_request/state.js5
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js47
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_comment_row.vue6
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue1
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue12
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue2
-rw-r--r--app/assets/javascripts/diffs/mixins/draft_comments.js2
-rw-r--r--app/assets/javascripts/event_tracking/notes.js1
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue4
-rw-r--r--app/assets/javascripts/ide/lib/files.js5
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js26
-rw-r--r--app/assets/javascripts/issuable_bulk_update_actions.js4
-rw-r--r--app/assets/javascripts/issuable_index.js17
-rw-r--r--app/assets/javascripts/issuable_init_bulk_update_sidebar.js19
-rw-r--r--app/assets/javascripts/labels_select.js4
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js8
-rw-r--r--app/assets/javascripts/main.js6
-rw-r--r--app/assets/javascripts/main_ee.js1
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue17
-rw-r--r--app/assets/javascripts/monitoring/components/empty_state.vue65
-rw-r--r--app/assets/javascripts/monitoring/stores/utils.js17
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue54
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue2
-rw-r--r--app/assets/javascripts/notes/components/discussion_notes.vue8
-rw-r--r--app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue27
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue16
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue32
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_signed_out_widget.vue19
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue20
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue18
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue3
-rw-r--r--app/assets/javascripts/notes/index.js5
-rw-r--r--app/assets/javascripts/notes/services/notes_service.js4
-rw-r--r--app/assets/javascripts/notes/stores/actions.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js4
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/form.js4
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue29
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue19
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/spent_only_pane.vue19
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/todo.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue13
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue43
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/changed_file_icon.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/deprecated_modal.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_warning.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue30
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestions.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue25
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/image.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue3
-rw-r--r--app/assets/javascripts/vue_shared/mixins/is_ee.js10
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/modal.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/diff.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss15
-rw-r--r--app/assets/stylesheets/pages/wiki.scss4
-rw-r--r--app/controllers/concerns/issuable_actions.rb4
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/graphql_controller.rb5
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/deployments_controller.rb14
-rw-r--r--app/controllers/projects/issues_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/finders/issuable_finder.rb2
-rw-r--r--app/finders/runner_jobs_finder.rb19
-rw-r--r--app/graphql/gitlab_schema.rb1
-rw-r--r--app/graphql/types/base_field.rb19
-rw-r--r--app/graphql/types/merge_request_type.rb2
-rw-r--r--app/graphql/types/mutation_type.rb2
-rw-r--r--app/graphql/types/permission_types/merge_request.rb4
-rw-r--r--app/graphql/types/project_type.rb4
-rw-r--r--app/graphql/types/repository_type.rb6
-rw-r--r--app/graphql/types/tree/tree_type.rb6
-rw-r--r--app/helpers/application_settings_helper.rb2
-rw-r--r--app/helpers/auth_helper.rb2
-rw-r--r--app/helpers/issuables_helper.rb4
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/helpers/preferences_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/storage_helper.rb2
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/clusters/clusters_hierarchy.rb104
-rw-r--r--app/models/concerns/deployment_platform.rb19
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/concerns/reactive_caching.rb8
-rw-r--r--app/models/deployment.rb42
-rw-r--r--app/models/deployment_metrics.rb61
-rw-r--r--app/models/environment_status.rb18
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/label_note.rb18
-rw-r--r--app/models/merge_request.rb41
-rw-r--r--app/models/namespace/aggregation_schedule.rb8
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/drone_ci_service.rb4
-rw-r--r--app/models/project_services/kubernetes_service.rb129
-rw-r--r--app/models/project_statistics.rb25
-rw-r--r--app/models/repository.rb8
-rw-r--r--app/models/resource_label_event.rb7
-rw-r--r--app/serializers/environment_status_entity.rb6
-rw-r--r--app/serializers/test_suite_comparer_entity.rb29
-rw-r--r--app/services/auto_merge/base_service.rb14
-rw-r--r--app/services/auto_merge/merge_when_pipeline_succeeds_service.rb10
-rw-r--r--app/services/auto_merge_service.rb6
-rw-r--r--app/services/ci/compare_reports_base_service.rb6
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/discussions/update_diff_position_service.rb3
-rw-r--r--app/services/issuable/bulk_update_service.rb10
-rw-r--r--app/services/issuable_base_service.rb4
-rw-r--r--app/services/issues/update_service.rb4
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/merge_to_ref_service.rb16
-rw-r--r--app/services/merge_requests/rebase_service.rb4
-rw-r--r--app/services/merge_requests/refresh_service.rb6
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/prometheus/adapter_service.rb7
-rw-r--r--app/services/system_note_service.rb22
-rw-r--r--app/uploaders/file_uploader.rb2
-rw-r--r--app/uploaders/personal_file_uploader.rb2
-rw-r--r--app/views/admin/application_settings/_grafana.html.haml17
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml11
-rw-r--r--app/views/admin/groups/show.html.haml6
-rw-r--r--app/views/admin/projects/show.html.haml6
-rw-r--r--app/views/admin/users/show.html.haml2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml24
-rw-r--r--app/views/groups/issues.html.haml9
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/layouts/fullscreen.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml5
-rw-r--r--app/views/profiles/preferences/show.html.haml12
-rw-r--r--app/views/projects/_flash_messages.html.haml1
-rw-r--r--app/views/projects/issues/_new_branch.html.haml13
-rw-r--r--app/views/shared/_storage_counter_statistics.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml3
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/workers/concerns/application_worker.rb6
-rw-r--r--app/workers/rebase_worker.rb2
-rw-r--r--changelogs/unreleased/12533-shared-runners-warning.yml5
-rw-r--r--changelogs/unreleased/12550-fullscrean.yml5
-rw-r--r--changelogs/unreleased/12553-preferences.yml5
-rw-r--r--changelogs/unreleased/40379-CJK-search-min-chars.yml5
-rw-r--r--changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml5
-rw-r--r--changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml5
-rw-r--r--changelogs/unreleased/54117-transactional-rebase.yml5
-rw-r--r--changelogs/unreleased/57793-fix-line-age.yml5
-rw-r--r--changelogs/unreleased/60856-deleting-binary-file.yml5
-rw-r--r--changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml5
-rw-r--r--changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml5
-rw-r--r--changelogs/unreleased/63475-fix-n-1.yml5
-rw-r--r--changelogs/unreleased/63873-process-start-time.yml6
-rw-r--r--changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml5
-rw-r--r--changelogs/unreleased/64176-fix-error-handling.yml5
-rw-r--r--changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml5
-rw-r--r--changelogs/unreleased/add-salesforce-logo.yml5
-rw-r--r--changelogs/unreleased/allow-reactive-caching-of-nil.yml5
-rw-r--r--changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml5
-rw-r--r--changelogs/unreleased/centralize-markdownlint-config.yml5
-rw-r--r--changelogs/unreleased/clusters-group-cte.yml5
-rw-r--r--changelogs/unreleased/create-merge-train-ref-ce.yml5
-rw-r--r--changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml5
-rw-r--r--changelogs/unreleased/fix-sidekiq-transaction-check-race.yml5
-rw-r--r--changelogs/unreleased/fj-fix-subgroup-search-url.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.51.0.yml5
-rw-r--r--changelogs/unreleased/issue-63222.yml5
-rw-r--r--changelogs/unreleased/issue_64021.yml5
-rw-r--r--changelogs/unreleased/jc-detect-nfs-for-rugged.yml5
-rw-r--r--changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml5
-rw-r--r--changelogs/unreleased/limit-amount-of-tests-returned.yml5
-rw-r--r--changelogs/unreleased/patch-29.yml5
-rw-r--r--changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml5
-rw-r--r--changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-63349.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml5
-rw-r--r--changelogs/unreleased/tz-update-mr-count-over-tabs.yml6
-rw-r--r--changelogs/unreleased/update-todo-in-ui.yml5
-rw-r--r--changelogs/unreleased/winh-notes-service-applySuggestion.yml5
-rw-r--r--config/README.md3
-rw-r--r--config/application.rb1
-rw-r--r--config/initializers/0_license.rb19
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/console_message.rb13
-rw-r--r--config/initializers/elastic_client_setup.rb50
-rw-r--r--config/initializers/forbid_sidekiq_in_transactions.rb13
-rw-r--r--config/initializers/geo.rb17
-rw-r--r--config/initializers/health_check.rb6
-rw-r--r--config/initializers/load_balancing.rb25
-rw-r--r--config/initializers/peek.rb3
-rw-r--r--config/initializers/rack_attack_logging.rb2
-rw-r--r--config/initializers/sidekiq.rb13
-rw-r--r--config/initializers/sidekiq_cluster.rb20
-rw-r--r--config/no_todos_messages.yml8
-rw-r--r--config/routes/api.rb6
-rw-r--r--config/webpack.config.js3
-rw-r--r--danger/database/Dangerfile32
-rw-r--r--danger/only_documentation/Dangerfile2
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb12
-rw-r--r--db/migrate/20190617123615_add_grafana_to_settings.rb18
-rw-r--r--db/migrate/20190621022810_add_last_ci_minutes_usage_notification_level_to_namespaces.rb9
-rw-r--r--db/migrate/20190621151636_add_merge_request_rebase_jid.rb9
-rw-r--r--db/migrate/20190624123615_add_grafana_url_to_settings.rb18
-rw-r--r--db/migrate/20190703130053_remove_gitaly_feature_flags.rb48
-rw-r--r--db/schema.rb6
-rw-r--r--doc/README.md62
-rw-r--r--doc/administration/audit_events.md10
-rw-r--r--doc/administration/auditor_users.md4
-rw-r--r--doc/administration/auth/README.md4
-rw-r--r--doc/administration/auth/google_secure_ldap.md5
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md4
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md2
-rw-r--r--doc/administration/auth/ldap-ee.md2
-rw-r--r--doc/administration/auth/ldap.md8
-rw-r--r--doc/administration/auth/smartcard.md30
-rw-r--r--doc/administration/container_registry.md300
-rw-r--r--doc/administration/database_load_balancing.md30
-rw-r--r--doc/administration/dependency_proxy.md4
-rw-r--r--doc/administration/geo/disaster_recovery/background_verification.md2
-rw-r--r--doc/administration/geo/disaster_recovery/bring_primary_back.md2
-rw-r--r--doc/administration/geo/disaster_recovery/index.md2
-rw-r--r--doc/administration/geo/disaster_recovery/planned_failover.md2
-rw-r--r--doc/administration/geo/replication/configuration.md2
-rw-r--r--doc/administration/geo/replication/database.md2
-rw-r--r--doc/administration/geo/replication/docker_registry.md2
-rw-r--r--doc/administration/geo/replication/external_database.md2
-rw-r--r--doc/administration/geo/replication/faq.md2
-rw-r--r--doc/administration/geo/replication/high_availability.md2
-rw-r--r--doc/administration/geo/replication/index.md4
-rw-r--r--doc/administration/geo/replication/object_storage.md6
-rw-r--r--doc/administration/geo/replication/remove_geo_node.md2
-rw-r--r--doc/administration/geo/replication/security_review.md4
-rw-r--r--doc/administration/geo/replication/troubleshooting.md2
-rw-r--r--doc/administration/geo/replication/tuning.md2
-rw-r--r--doc/administration/geo/replication/updating_the_geo_nodes.md2
-rw-r--r--doc/administration/geo/replication/using_a_geo_server.md2
-rw-r--r--doc/administration/gitaly/index.md24
-rw-r--r--doc/administration/high_availability/consul.md70
-rw-r--r--doc/administration/high_availability/database.md77
-rw-r--r--doc/administration/high_availability/gitlab.md2
-rw-r--r--doc/administration/high_availability/monitoring_node.md2
-rw-r--r--doc/administration/high_availability/redis.md16
-rw-r--r--doc/administration/img/audit_log.png (renamed from doc/administration/audit_log.png)bin25767 -> 25767 bytes
-rw-r--r--doc/administration/img/auditor_access_form.png (renamed from doc/administration/auditor_access_form.png)bin11910 -> 11910 bytes
-rw-r--r--doc/administration/incoming_email.md44
-rw-r--r--doc/administration/index.md48
-rw-r--r--doc/administration/instance_review.md2
-rw-r--r--doc/administration/integration/plantuml.md2
-rw-r--r--doc/administration/issue_closing_pattern.md17
-rw-r--r--doc/administration/job_artifacts.md154
-rw-r--r--doc/administration/job_traces.md48
-rw-r--r--doc/administration/logs.md5
-rw-r--r--doc/administration/merge_request_diffs.md130
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md19
-rw-r--r--doc/administration/monitoring/performance/influxdb_configuration.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md4
-rw-r--r--doc/administration/operations/extra_sidekiq_processes.md2
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md4
-rw-r--r--doc/administration/operations/index.md2
-rw-r--r--doc/administration/operations/unicorn.md2
-rw-r--r--doc/administration/packages.md144
-rw-r--r--doc/administration/pseudonymizer.md70
-rw-r--r--doc/administration/raketasks/geo.md2
-rw-r--r--doc/administration/raketasks/maintenance.md2
-rw-r--r--doc/administration/raketasks/project_import_export.md2
-rw-r--r--doc/administration/reply_by_email_postfix_setup.md384
-rw-r--r--doc/administration/repository_storage_paths.md42
-rw-r--r--doc/administration/repository_storage_types.md33
-rw-r--r--doc/administration/restart_gitlab.md4
-rw-r--r--doc/administration/uploads.md90
-rw-r--r--doc/api/README.md38
-rw-r--r--doc/api/boards.md11
-rw-r--r--doc/api/deploy_keys.md4
-rw-r--r--doc/api/discussions.md4
-rw-r--r--doc/api/epic_issues.md2
-rw-r--r--doc/api/epic_links.md2
-rw-r--r--doc/api/epics.md4
-rw-r--r--doc/api/geo_nodes.md2
-rw-r--r--doc/api/graphql/index.md18
-rw-r--r--doc/api/group_boards.md54
-rw-r--r--doc/api/group_milestones.md16
-rw-r--r--doc/api/groups.md159
-rw-r--r--doc/api/issue_links.md5
-rw-r--r--doc/api/issues.md38
-rw-r--r--doc/api/issues_statistics.md19
-rw-r--r--doc/api/jobs.md11
-rw-r--r--doc/api/license.md3
-rw-r--r--doc/api/lint.md38
-rw-r--r--doc/api/managed_licenses.md4
-rw-r--r--doc/api/merge_request_approvals.md6
-rw-r--r--doc/api/merge_requests.md36
-rw-r--r--doc/api/milestones.md16
-rw-r--r--doc/api/namespaces.md2
-rw-r--r--doc/api/notes.md4
-rw-r--r--doc/api/notification_settings.md11
-rw-r--r--doc/api/oauth2.md2
-rw-r--r--doc/api/packages.md4
-rw-r--r--doc/api/pages_domains.md2
-rw-r--r--doc/api/pipeline_schedules.md12
-rw-r--r--doc/api/pipelines.md10
-rw-r--r--doc/api/project_aliases.md2
-rw-r--r--doc/api/project_badges.md2
-rw-r--r--doc/api/project_clusters.md4
-rw-r--r--doc/api/project_level_variables.md4
-rw-r--r--doc/api/project_snippets.md4
-rw-r--r--doc/api/project_statistics.md2
-rw-r--r--doc/api/project_templates.md6
-rw-r--r--doc/api/projects.md112
-rw-r--r--doc/api/protected_branches.md8
-rw-r--r--doc/api/repositories.md6
-rw-r--r--doc/api/resource_label_events.md2
-rw-r--r--doc/api/runners.md46
-rw-r--r--doc/api/scim.md4
-rw-r--r--doc/api/search.md16
-rw-r--r--doc/api/services.md4
-rw-r--r--doc/api/settings.md79
-rw-r--r--doc/api/users.md191
-rw-r--r--doc/api/v3_to_v4.md4
-rw-r--r--doc/api/vulnerabilities.md16
-rw-r--r--doc/ci/README.md22
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md2
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md4
-rw-r--r--doc/ci/ci_cd_for_external_repos/index.md2
-rw-r--r--doc/ci/docker/using_docker_images.md18
-rw-r--r--doc/ci/environments.md6
-rw-r--r--doc/ci/environments/protected_environments.md2
-rw-r--r--doc/ci/examples/README.md4
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md2
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md8
-rw-r--r--doc/ci/examples/php.md6
-rw-r--r--doc/ci/examples/test-clojure-application.md2
-rw-r--r--doc/ci/examples/test-scala-application.md2
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md10
-rw-r--r--doc/ci/git_submodules.md16
-rw-r--r--doc/ci/interactive_web_terminal/index.md2
-rw-r--r--doc/ci/introduction/index.md18
-rw-r--r--doc/ci/merge_request_pipelines/index.md4
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md4
-rw-r--r--doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md2
-rw-r--r--doc/ci/metrics_reports.md4
-rw-r--r--doc/ci/multi_project_pipelines.md2
-rw-r--r--doc/ci/pipelines.md2
-rw-r--r--doc/ci/quick_start/README.md2
-rw-r--r--doc/ci/review_apps/img/toolbar_feeback_form.pngbin71676 -> 24599 bytes
-rw-r--r--doc/ci/review_apps/index.md126
-rw-r--r--doc/ci/services/postgres.md2
-rw-r--r--doc/ci/triggers/README.md4
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/yaml/README.md32
-rw-r--r--doc/customization/index.md2
-rw-r--r--doc/customization/issue_closing.md4
-rw-r--r--doc/customization/libravatar.md12
-rw-r--r--doc/customization/system_header_and_footer_messages.md4
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/api_graphql_styleguide.md15
-rw-r--r--doc/development/api_styleguide.md2
-rw-r--r--doc/development/architecture.md10
-rw-r--r--doc/development/automatic_ce_ee_merge.md53
-rw-r--r--doc/development/changelog.md16
-rw-r--r--doc/development/chatops_on_gitlabcom.md8
-rw-r--r--doc/development/code_comments.md6
-rw-r--r--doc/development/code_review.md2
-rw-r--r--doc/development/contributing/community_roles.md4
-rw-r--r--doc/development/contributing/index.md2
-rw-r--r--doc/development/contributing/issue_workflow.md17
-rw-r--r--doc/development/contributing/merge_request_workflow.md3
-rw-r--r--doc/development/contributing/style_guides.md8
-rw-r--r--doc/development/database_debugging.md2
-rw-r--r--doc/development/documentation/styleguide.md36
-rw-r--r--doc/development/ee_features.md79
-rw-r--r--doc/development/elasticsearch.md4
-rw-r--r--doc/development/emails.md96
-rw-r--r--doc/development/fe_guide/architecture.md2
-rw-r--r--doc/development/fe_guide/development_process.md24
-rw-r--r--doc/development/fe_guide/emojis.md4
-rw-r--r--doc/development/fe_guide/graphql.md4
-rw-r--r--doc/development/fe_guide/security.md7
-rw-r--r--doc/development/feature_flags/controls.md4
-rw-r--r--doc/development/feature_flags/development.md2
-rw-r--r--doc/development/file_storage.md6
-rw-r--r--doc/development/geo.md2
-rw-r--r--doc/development/go_guide/index.md6
-rw-r--r--doc/development/gotchas.md8
-rw-r--r--doc/development/integrations/jira_connect.md4
-rw-r--r--doc/development/licensed_feature_availability.md22
-rw-r--r--doc/development/logging.md82
-rw-r--r--doc/development/migration_style_guide.md2
-rw-r--r--doc/development/new_fe_guide/development/components.md2
-rw-r--r--doc/development/new_fe_guide/development/performance.md4
-rw-r--r--doc/development/new_fe_guide/development/testing.md4
-rw-r--r--doc/development/new_fe_guide/style/html.md2
-rw-r--r--doc/development/newlines_styleguide.md4
-rw-r--r--doc/development/packages.md40
-rw-r--r--doc/development/profiling.md8
-rw-r--r--doc/development/python_guide/index.md3
-rw-r--r--doc/development/query_recorder.md4
-rw-r--r--doc/development/rake_tasks.md2
-rw-r--r--doc/development/routing.md34
-rw-r--r--doc/development/sql.md4
-rw-r--r--doc/development/testing_guide/ci.md4
-rw-r--r--doc/development/testing_guide/end_to_end/index.md2
-rw-r--r--doc/development/testing_guide/end_to_end/quick_start_guide.md8
-rw-r--r--doc/development/testing_guide/frontend_testing.md1
-rw-r--r--doc/development/testing_guide/index.md5
-rw-r--r--doc/development/understanding_explain_plans.md1
-rw-r--r--doc/development/ux_guide/resources.md4
-rw-r--r--doc/downgrade_ee_to_ce/README.md2
-rw-r--r--doc/gitlab-basics/README.md2
-rw-r--r--doc/gitlab-basics/create-issue.md4
-rw-r--r--doc/gitlab-basics/create-project.md4
-rw-r--r--doc/install/README.md2
-rw-r--r--doc/install/google_cloud_platform/index.md8
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/install/openshift_and_gitlab/index.md17
-rw-r--r--doc/install/pivotal/index.md2
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/integration/akismet.md2
-rw-r--r--doc/integration/azure.md96
-rw-r--r--doc/integration/elasticsearch.md2
-rw-r--r--doc/integration/jenkins.md2
-rw-r--r--doc/integration/jira_development_panel.md2
-rw-r--r--doc/integration/kerberos.md2
-rw-r--r--doc/integration/oauth2_generic.md10
-rw-r--r--doc/integration/oauth_provider.md4
-rw-r--r--doc/integration/openid_connect_provider.md6
-rw-r--r--doc/integration/shibboleth.md4
-rw-r--r--doc/integration/twitter.md99
-rw-r--r--doc/intro/README.md6
-rw-r--r--doc/policy/maintenance.md2
-rw-r--r--doc/push_rules/push_rules.md2
-rw-r--r--doc/raketasks/README.md4
-rw-r--r--doc/raketasks/web_hooks.md2
-rw-r--r--doc/tools/email.md2
-rw-r--r--doc/topics/application_development_platform/index.md30
-rw-r--r--doc/topics/authentication/index.md10
-rw-r--r--doc/topics/autodevops/index.md38
-rw-r--r--doc/topics/autodevops/quick_start_guide.md14
-rw-r--r--doc/university/README.md2
-rw-r--r--doc/university/support/README.md2
-rw-r--r--doc/university/training/index.md6
-rw-r--r--doc/update/mysql_to_postgresql.md4
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/update/upgrading_from_ce_to_ee.md2
-rw-r--r--doc/update/upgrading_from_source.md2
-rw-r--r--doc/user/admin_area/custom_project_templates.md4
-rw-r--r--doc/user/admin_area/geo_nodes.md10
-rw-r--r--doc/user/admin_area/index.md10
-rw-r--r--doc/user/admin_area/labels.md2
-rw-r--r--doc/user/admin_area/license.md2
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md6
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md12
-rw-r--r--doc/user/admin_area/settings/email.md2
-rw-r--r--doc/user/admin_area/settings/external_authorization.md4
-rw-r--r--doc/user/admin_area/settings/index.md6
-rw-r--r--doc/user/admin_area/settings/instance_template_repository.md4
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md6
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md2
-rw-r--r--doc/user/application_security/container_scanning/index.md2
-rw-r--r--doc/user/application_security/dast/index.md2
-rw-r--r--doc/user/application_security/dependency_scanning/analyzers.md2
-rw-r--r--doc/user/application_security/dependency_scanning/index.md2
-rw-r--r--doc/user/application_security/index.md14
-rw-r--r--doc/user/application_security/license_management/index.md13
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--doc/user/application_security/security_dashboard/index.md2
-rw-r--r--doc/user/asciidoc.md2
-rw-r--r--doc/user/discussions/index.md4
-rw-r--r--doc/user/gitlab_com/index.md20
-rw-r--r--doc/user/group/clusters/index.md4
-rw-r--r--doc/user/group/contribution_analytics/index.md2
-rw-r--r--doc/user/group/custom_project_templates.md4
-rw-r--r--doc/user/group/dependency_proxy/index.md2
-rw-r--r--doc/user/group/epics/index.md2
-rw-r--r--doc/user/group/index.md24
-rw-r--r--doc/user/group/insights/index.md2
-rw-r--r--doc/user/group/issues_analytics/index.md2
-rw-r--r--doc/user/group/roadmap/index.md6
-rw-r--r--doc/user/group/saml_sso/index.md2
-rw-r--r--doc/user/group/saml_sso/scim_setup.md2
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/markdown.md4
-rw-r--r--doc/user/operations_dashboard/index.md2
-rw-r--r--doc/user/permissions.md38
-rw-r--r--doc/user/profile/preferences.md2
-rw-r--r--doc/user/project/canary_deployments.md2
-rw-r--r--doc/user/project/clusters/index.md15
-rw-r--r--doc/user/project/clusters/kubernetes_pod_logs.md2
-rw-r--r--doc/user/project/code_owners.md17
-rw-r--r--doc/user/project/container_registry.md2
-rw-r--r--doc/user/project/cycle_analytics.md2
-rw-r--r--doc/user/project/deploy_boards.md4
-rw-r--r--doc/user/project/deploy_tokens/index.md2
-rw-r--r--doc/user/project/description_templates.md2
-rw-r--r--doc/user/project/file_lock.md2
-rw-r--r--doc/user/project/import/gemnasium.md2
-rw-r--r--doc/user/project/import/github.md4
-rw-r--r--doc/user/project/import/index.md2
-rw-r--r--doc/user/project/import/svn.md2
-rw-r--r--doc/user/project/import/tfs.md10
-rw-r--r--doc/user/project/index.md24
-rw-r--r--doc/user/project/insights/index.md2
-rw-r--r--doc/user/project/integrations/github.md4
-rw-r--r--doc/user/project/integrations/img/jira_api_token.pngbin61394 -> 21318 bytes
-rw-r--r--doc/user/project/integrations/img/jira_api_token_menu.pngbin25056 -> 41876 bytes
-rw-r--r--doc/user/project/integrations/img/jira_issue_reference.pngbin18399 -> 64064 bytes
-rw-r--r--doc/user/project/integrations/img/jira_merge_request_close.pngbin21172 -> 64305 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_close_comment.pngbin11890 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_close_issue.pngbin30570 -> 29632 bytes
-rw-r--r--doc/user/project/integrations/img/jira_service_page.pngbin30395 -> 70807 bytes
-rw-r--r--doc/user/project/integrations/jira.md26
-rw-r--r--doc/user/project/integrations/jira_cloud_configuration.md16
-rw-r--r--doc/user/project/integrations/project_services.md6
-rw-r--r--doc/user/project/integrations/prometheus.md15
-rw-r--r--doc/user/project/integrations/prometheus_library/cloudwatch.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/kubernetes.md2
-rw-r--r--doc/user/project/integrations/slack.md2
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md2
-rw-r--r--doc/user/project/integrations/webhooks.md2
-rw-r--r--doc/user/project/issue_board.md18
-rw-r--r--doc/user/project/issues/automatic_issue_closing.md62
-rw-r--r--doc/user/project/issues/closing_issues.md62
-rw-r--r--doc/user/project/issues/create_new_issue.md107
-rw-r--r--doc/user/project/issues/crosslinking_issues.md23
-rw-r--r--doc/user/project/issues/csv_export.md2
-rw-r--r--doc/user/project/issues/csv_import.md55
-rw-r--r--doc/user/project/issues/deleting_issues.md16
-rw-r--r--doc/user/project/issues/due_dates.md29
-rw-r--r--doc/user/project/issues/index.md99
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md29
-rw-r--r--doc/user/project/issues/managing_issues.md225
-rw-r--r--doc/user/project/issues/moving_issues.md38
-rw-r--r--doc/user/project/issues/multiple_assignees_for_issues.md2
-rw-r--r--doc/user/project/issues/related_issues.md2
-rw-r--r--doc/user/project/issues/similar_issues.md19
-rw-r--r--doc/user/project/labels.md10
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md4
-rw-r--r--doc/user/project/merge_requests/code_quality.md2
-rw-r--r--doc/user/project/merge_requests/index.md40
-rw-r--r--doc/user/project/merge_requests/merge_request_approvals.md12
-rw-r--r--doc/user/project/milestones/burndown_charts.md2
-rw-r--r--doc/user/project/milestones/index.md10
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md2
-rw-r--r--doc/user/project/operations/feature_flags.md2
-rw-r--r--doc/user/project/operations/index.md4
-rw-r--r--doc/user/project/operations/tracing.md10
-rw-r--r--doc/user/project/packages/maven_repository.md4
-rw-r--r--doc/user/project/packages/npm_registry.md19
-rw-r--r--doc/user/project/pages/getting_started_part_four.md2
-rw-r--r--doc/user/project/pages/index.md2
-rw-r--r--doc/user/project/pages/introduction.md2
-rw-r--r--doc/user/project/pipelines/job_artifacts.md11
-rw-r--r--doc/user/project/protected_branches.md2
-rw-r--r--doc/user/project/quick_actions.md30
-rw-r--r--doc/user/project/repository/branches/index.md2
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md2
-rw-r--r--doc/user/project/repository/index.md15
-rw-r--r--doc/user/project/repository/web_editor.md3
-rw-r--r--doc/user/project/service_desk.md2
-rw-r--r--doc/user/project/settings/import_export.md7
-rw-r--r--doc/user/project/settings/index.md12
-rw-r--r--doc/user/project/web_ide/index.md4
-rw-r--r--doc/user/search/advanced_global_search.md2
-rw-r--r--doc/user/search/advanced_search_syntax.md2
-rw-r--r--doc/user/search/index.md10
-rw-r--r--doc/workflow/README.md14
-rw-r--r--doc/workflow/gitlab_flow.md35
-rw-r--r--doc/workflow/img/ci_mr.png (renamed from doc/workflow/ci_mr.png)bin12024 -> 12024 bytes
-rw-r--r--doc/workflow/img/close_issue_mr.png (renamed from doc/workflow/close_issue_mr.png)bin42108 -> 42108 bytes
-rw-r--r--doc/workflow/img/environment_branches.png (renamed from doc/workflow/environment_branches.png)bin12354 -> 12354 bytes
-rw-r--r--doc/workflow/img/four_stages.png (renamed from doc/workflow/four_stages.png)bin7124 -> 7124 bytes
-rw-r--r--doc/workflow/img/git_pull.png (renamed from doc/workflow/git_pull.png)bin28701 -> 28701 bytes
-rw-r--r--doc/workflow/img/gitdashflow.png (renamed from doc/workflow/gitdashflow.png)bin68177 -> 68177 bytes
-rw-r--r--doc/workflow/img/github_flow.png (renamed from doc/workflow/github_flow.png)bin6173 -> 6173 bytes
-rw-r--r--doc/workflow/img/gitlab_flow.png (renamed from doc/workflow/gitlab_flow.png)bin47430 -> 47430 bytes
-rw-r--r--doc/workflow/img/good_commit.png (renamed from doc/workflow/good_commit.png)bin8740 -> 8740 bytes
-rw-r--r--doc/workflow/img/merge_commits.png (renamed from doc/workflow/merge_commits.png)bin7564 -> 7564 bytes
-rw-r--r--doc/workflow/img/merge_request.png (renamed from doc/workflow/merge_request.png)bin47225 -> 47225 bytes
-rw-r--r--doc/workflow/img/messy_flow.png (renamed from doc/workflow/messy_flow.png)bin11663 -> 11663 bytes
-rw-r--r--doc/workflow/img/mr_inline_comments.png (renamed from doc/workflow/mr_inline_comments.png)bin52503 -> 52503 bytes
-rw-r--r--doc/workflow/img/production_branch.png (renamed from doc/workflow/production_branch.png)bin7262 -> 7262 bytes
-rw-r--r--doc/workflow/img/rebase.png (renamed from doc/workflow/rebase.png)bin28939 -> 28939 bytes
-rw-r--r--doc/workflow/img/release_branches.png (renamed from doc/workflow/release_branches.png)bin12736 -> 12736 bytes
-rw-r--r--doc/workflow/img/remove_checkbox.png (renamed from doc/workflow/remove_checkbox.png)bin6904 -> 6904 bytes
-rw-r--r--doc/workflow/issue_weight.md2
-rw-r--r--doc/workflow/lfs/lfs_administration.md2
-rw-r--r--doc/workflow/notifications.md12
-rw-r--r--doc/workflow/repository_mirroring.md30
-rw-r--r--doc/workflow/shortcuts.md2
-rw-r--r--doc/workflow/timezone.md8
-rw-r--r--doc/workflow/todos.md140
-rw-r--r--lib/api/api.rb6
-rw-r--r--lib/api/helpers/graphql_helpers.rb2
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/runners.rb2
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/user_counts.rb18
-rw-r--r--lib/feature.rb23
-rw-r--r--lib/feature/gitaly.rb7
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/PHP.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml1
-rw-r--r--lib/gitlab/danger/helper.rb6
-rw-r--r--lib/gitlab/database/median.rb2
-rw-r--r--lib/gitlab/diff/position.rb4
-rw-r--r--lib/gitlab/diff/position_tracer.rb192
-rw-r--r--lib/gitlab/diff/position_tracer/base_strategy.rb26
-rw-r--r--lib/gitlab/diff/position_tracer/image_strategy.rb50
-rw-r--r--lib/gitlab/diff/position_tracer/line_strategy.rb201
-rw-r--r--lib/gitlab/git/repository.rb4
-rw-r--r--lib/gitlab/git/rugged_impl/blob.rb3
-rw-r--r--lib/gitlab/git/rugged_impl/commit.rb8
-rw-r--r--lib/gitlab/git/rugged_impl/repository.rb3
-rw-r--r--lib/gitlab/git/rugged_impl/tree.rb3
-rw-r--r--lib/gitlab/git/rugged_impl/use_rugged.rb16
-rw-r--r--lib/gitlab/gitaly_client.rb49
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb5
-rw-r--r--lib/gitlab/gon_helper.rb5
-rw-r--r--lib/gitlab/graphql.rb4
-rw-r--r--lib/gitlab/graphql/authorize.rb2
-rw-r--r--lib/gitlab/graphql/calls_gitaly.rb15
-rw-r--r--lib/gitlab/graphql/calls_gitaly/instrumentation.rb40
-rw-r--r--lib/gitlab/graphql/mount_mutation.rb5
-rw-r--r--lib/gitlab/http.rb6
-rw-r--r--lib/gitlab/import_export/import_export.yml1
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb7
-rw-r--r--lib/gitlab/metrics/system.rb12
-rw-r--r--lib/gitlab/namespaced_session_store.rb15
-rw-r--r--lib/gitlab/performance_bar.rb28
-rw-r--r--lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb12
-rw-r--r--lib/gitlab/quick_actions/issuable_actions.rb4
-rw-r--r--lib/gitlab/sidekiq_status.rb6
-rw-r--r--lib/gitlab/sql/pattern.rb24
-rw-r--r--lib/gitlab/user_extractor.rb56
-rw-r--r--lib/peek/views/redis.rb8
-rw-r--r--locale/gitlab.pot270
-rw-r--r--package.json4
-rw-r--r--qa/.rspec_parallel5
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock6
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/page/project/show.rb1
-rw-r--r--qa/qa/page/project/sub_menus/ci_cd.rb2
-rw-r--r--qa/qa/page/project/sub_menus/issues.rb2
-rw-r--r--qa/qa/page/project/sub_menus/operations.rb2
-rw-r--r--qa/qa/page/project/sub_menus/repository.rb2
-rw-r--r--qa/qa/page/project/sub_menus/settings.rb2
-rw-r--r--qa/qa/resource/issue.rb6
-rw-r--r--qa/qa/runtime/browser.rb6
-rw-r--r--qa/qa/runtime/env.rb8
-rw-r--r--qa/qa/runtime/scenario.rb6
-rw-r--r--qa/qa/scenario/shared_attributes.rb1
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb44
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb24
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb21
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb55
-rw-r--r--qa/qa/specs/parallel_runner.rb33
-rw-r--r--qa/qa/specs/runner.rb58
-rw-r--r--qa/qa/tools/generate_perf_testdata.rb65
-rw-r--r--qa/spec/page/logging_spec.rb8
-rw-r--r--qa/spec/spec_helper.rb4
-rw-r--r--qa/spec/specs/parallel_runner_spec.rb58
-rw-r--r--qa/spec/specs/runner_spec.rb8
-rwxr-xr-xscripts/review_apps/review-apps.sh128
-rwxr-xr-xscripts/trigger-build-docs4
-rw-r--r--spec/controllers/dashboard/projects_controller_spec.rb18
-rw-r--r--spec/controllers/projects/deployments_controller_spec.rb74
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb16
-rw-r--r--spec/controllers/projects_controller_spec.rb47
-rw-r--r--spec/features/admin/admin_sees_project_statistics_spec.rb2
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb14
-rw-r--r--spec/features/issues/todo_spec.rb8
-rw-r--r--spec/features/issues/user_creates_confidential_merge_request_spec.rb54
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb62
-rw-r--r--spec/features/oauth_login_spec.rb12
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb124
-rw-r--r--spec/finders/runner_jobs_finder_spec.rb22
-rw-r--r--spec/frontend/api_spec.js16
-rw-r--r--spec/frontend/boards/services/board_service_spec.js4
-rw-r--r--spec/frontend/branches/divergence_graph_spec.js12
-rw-r--r--spec/frontend/commons/nav/user_merge_requests_spec.js113
-rw-r--r--spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap67
-rw-r--r--spec/frontend/confidential_merge_request/components/dropdown_spec.js56
-rw-r--r--spec/frontend/confidential_merge_request/components/project_form_group_spec.js77
-rw-r--r--spec/frontend/create_merge_request_dropdown_spec.js (renamed from spec/javascripts/create_merge_request_dropdown_spec.js)9
-rw-r--r--spec/frontend/ide/lib/files_spec.js4
-rw-r--r--spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap37
-rw-r--r--spec/frontend/monitoring/dashboard_state_spec.js43
-rw-r--r--spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js51
-rw-r--r--spec/graphql/gitlab_schema_spec.rb4
-rw-r--r--spec/graphql/types/base_field_spec.rb59
-rw-r--r--spec/helpers/preferences_helper_spec.rb2
-rw-r--r--spec/helpers/storage_helper_spec.rb2
-rw-r--r--spec/javascripts/boards/mock_data.js5
-rw-r--r--spec/javascripts/collapsed_sidebar_todo_spec.js16
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js3
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js40
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js20
-rw-r--r--spec/javascripts/issuable_spec.js12
-rw-r--r--spec/javascripts/monitoring/dashboard_state_spec.js101
-rw-r--r--spec/javascripts/monitoring/store/utils_spec.js37
-rw-r--r--spec/javascripts/notes/components/comment_form_spec.js15
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js8
-rw-r--r--spec/javascripts/sidebar/todo_spec.js8
-rw-r--r--spec/javascripts/test_bundle.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js3
-rw-r--r--spec/lib/feature_spec.rb62
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb23
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_stage_spec.rb34
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb21
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb13
-rw-r--r--spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb238
-rw-r--r--spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb1805
-rw-r--r--spec/lib/gitlab/diff/position_tracer_spec.rb1918
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb7
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb97
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb23
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb16
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb12
-rw-r--r--spec/lib/gitlab/namespaced_session_store_spec.rb30
-rw-r--r--spec/lib/gitlab/performance_bar_spec.rb27
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb12
-rw-r--r--spec/lib/gitlab/user_extractor_spec.rb78
-rw-r--r--spec/lib/peek/views/redis_detailed_spec.rb36
-rw-r--r--spec/migrations/backfill_store_project_full_path_in_repo_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb24
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb73
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb18
-rw-r--r--spec/models/concerns/issuable_spec.rb10
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb61
-rw-r--r--spec/models/deployment_metrics_spec.rb126
-rw-r--r--spec/models/deployment_spec.rb153
-rw-r--r--spec/models/environment_status_spec.rb3
-rw-r--r--spec/models/merge_request_spec.rb145
-rw-r--r--spec/models/namespace/aggregation_schedule_spec.rb31
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb9
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb229
-rw-r--r--spec/models/project_statistics_spec.rb43
-rw-r--r--spec/models/repository_spec.rb5
-rw-r--r--spec/requests/api/graphql_spec.rb41
-rw-r--r--spec/requests/api/merge_requests_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/runners_spec.rb44
-rw-r--r--spec/requests/api/user_counts_spec.rb40
-rw-r--r--spec/routing/api_routing_spec.rb23
-rw-r--r--spec/serializers/environment_status_entity_spec.rb10
-rw-r--r--spec/serializers/test_case_entity_spec.rb2
-rw-r--r--spec/serializers/test_reports_comparer_entity_spec.rb8
-rw-r--r--spec/serializers/test_reports_comparer_serializer_spec.rb8
-rw-r--r--spec/serializers/test_suite_comparer_entity_spec.rb92
-rw-r--r--spec/services/auto_merge/base_service_spec.rb45
-rw-r--r--spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb16
-rw-r--r--spec/services/auto_merge_service_spec.rb25
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb496
-rw-r--r--spec/services/issues/update_service_spec.rb16
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb64
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb28
-rw-r--r--spec/services/system_note_service_spec.rb42
-rw-r--r--spec/support/helpers/devise_helpers.rb12
-rw-r--r--spec/support/helpers/fake_u2f_device.rb4
-rw-r--r--spec/support/helpers/git_http_helpers.rb4
-rw-r--r--spec/support/helpers/login_helpers.rb12
-rw-r--r--spec/support/helpers/position_tracer_helpers.rb93
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb2
-rw-r--r--spec/support/test_reports/test_reports_helper.rb34
-rw-r--r--spec/uploaders/file_mover_spec.rb3
-rw-r--r--spec/uploaders/file_uploader_spec.rb62
-rw-r--r--spec/workers/namespaces/schedule_aggregation_worker_spec.rb39
-rw-r--r--spec/workers/project_cache_worker_spec.rb14
-rw-r--r--yarn.lock22
834 files changed, 11484 insertions, 7310 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 2b881d5f201..2612fd3371d 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -5,6 +5,7 @@ globals:
gl: false
gon: false
localStorage: false
+ IS_EE: false
plugins:
- import
- html
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index 6acbce6cf0c..5bc109f2b7f 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -66,9 +66,7 @@ docs lint:
- mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX
- cd /tmp/gitlab-docs
# Lint Markdown
- # https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md
- - bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX/**/*.md --ignore-front-matter --rules \
- MD004,MD032,MD034
+ - bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX -c $CI_PROJECT_DIR/.mdlrc
# Build HTML from Markdown
- bundle exec nanoc
# Check the internal links
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 01c96e06547..2d06a8acc58 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -139,7 +139,7 @@ setup-test-env:
rspec unit pg:
<<: *rspec-metadata-pg
- parallel: 25
+ parallel: 20
rspec integration pg:
<<: *rspec-metadata-pg
@@ -152,7 +152,7 @@ rspec system pg:
rspec unit pg-10:
<<: *rspec-metadata-pg-10
<<: *only-schedules-master
- parallel: 25
+ parallel: 20
rspec integration pg-10:
<<: *rspec-metadata-pg-10
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 61fd48fd72e..ce019de213b 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -77,7 +77,7 @@ schedule:review-build-cng:
.review-deploy-base: &review-deploy-base
<<: *review-base
allow_failure: true
- retry: 2
+ retry: 1
stage: review
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
diff --git a/.mdlrc b/.mdlrc
new file mode 100644
index 00000000000..b2127dadc22
--- /dev/null
+++ b/.mdlrc
@@ -0,0 +1,4 @@
+# See https://github.com/markdownlint/markdownlint/blob/master/docs/configuration.md
+
+ignore_front_matter true
+style File.expand_path('.mdlrc.style', __dir__)
diff --git a/.mdlrc.style b/.mdlrc.style
new file mode 100644
index 00000000000..30abf03f462
--- /dev/null
+++ b/.mdlrc.style
@@ -0,0 +1,7 @@
+# See https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md
+
+rule 'MD001'
+# False positives, see https://github.com/markdownlint/markdownlint/issues/261
+# rule 'MD004', style: :dash
+rule 'MD032'
+rule 'MD034'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 698570efb07..41714eefa97 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -466,12 +466,10 @@ Rails/LinkToBlank:
Rails/Presence:
Exclude:
- 'app/models/ci/pipeline.rb'
- - 'app/models/clusters/platforms/kubernetes.rb'
- 'app/models/concerns/mentionable.rb'
- 'app/models/project_services/hipchat_service.rb'
- 'app/models/project_services/irker_service.rb'
- 'app/models/project_services/jira_service.rb'
- - 'app/models/project_services/kubernetes_service.rb'
- 'app/models/project_services/packagist_service.rb'
- 'app/models/wiki_page.rb'
- 'lib/gitlab/github_import/importer/releases_importer.rb'
@@ -514,7 +512,6 @@ Security/YAMLLoad:
- 'spec/config/mail_room_spec.rb'
- 'spec/initializers/secret_token_spec.rb'
- 'spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb'
- - 'spec/models/project_services/kubernetes_service_spec.rb'
# Offense count: 34
# Configuration parameters: EnforcedStyle.
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7f3a46a841e..ba0a719118c 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.49.0
+1.51.0
diff --git a/Gemfile b/Gemfile
index 1264d75eac6..f32e899342b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -132,7 +132,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '0.0.9'
-gem 'rouge', '~> 3.1'
+gem 'rouge', '~> 3.5'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.3'
@@ -309,7 +309,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
- gem 'prometheus-client-mmap', '~> 0.9.6'
+ gem 'prometheus-client-mmap', '~> 0.9.8'
gem 'raindrops', '~> 0.18'
end
@@ -368,6 +368,7 @@ group :development, :test do
gem 'haml_lint', '~> 0.31.0', require: false
gem 'simplecov', '~> 0.16.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
+ gem 'mdl', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
@@ -419,7 +420,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# SSH host key support
-gem 'net-ssh', '~> 5.0'
+gem 'net-ssh', '~> 5.2'
gem 'sshkey', '~> 2.0'
# Required for ED25519 SSH host key support
@@ -429,7 +430,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 1.32.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 1.36.0', require: 'gitaly'
gem 'grpc', '~> 1.19.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5b648d43137..85b4c32f168 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -303,7 +303,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (1.32.0)
+ gitaly-proto (1.36.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-labkit (0.3.0)
@@ -459,6 +459,7 @@ GEM
kgio (2.11.2)
knapsack (1.17.0)
rake
+ kramdown (1.17.0)
kubeclient (4.2.2)
http (~> 3.0)
recursive-open-struct (~> 1.0, >= 1.0.4)
@@ -492,6 +493,10 @@ GEM
mail (2.7.1)
mini_mime (>= 0.1.1)
mail_room (0.9.1)
+ mdl (0.5.0)
+ kramdown (~> 1.12, >= 1.12.0)
+ mixlib-cli (~> 1.7, >= 1.7.0)
+ mixlib-config (~> 2.2, >= 2.2.1)
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
@@ -505,6 +510,9 @@ GEM
mini_mime (1.0.1)
mini_portile2 (2.4.0)
minitest (5.11.3)
+ mixlib-cli (1.7.0)
+ mixlib-config (2.2.18)
+ tomlrb
msgpack (1.2.10)
multi_json (1.13.1)
multi_xml (0.6.0)
@@ -515,7 +523,7 @@ GEM
mysql2 (0.4.10)
nakayoshi_fork (0.0.4)
net-ldap (0.16.0)
- net-ssh (5.0.1)
+ net-ssh (5.2.0)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.10.3)
@@ -652,7 +660,7 @@ GEM
parser
unparser
procto (0.0.3)
- prometheus-client-mmap (0.9.6)
+ prometheus-client-mmap (0.9.8)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
@@ -770,7 +778,7 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
- rouge (3.4.1)
+ rouge (3.5.1)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@@ -943,6 +951,7 @@ GEM
parslet (~> 1.8.0)
toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
+ tomlrb (1.2.8)
truncato (0.7.11)
htmlentities (~> 4.3.1)
nokogiri (>= 1.7.0, <= 2.0)
@@ -1092,7 +1101,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 1.32.0)
+ gitaly-proto (~> 1.36.0)
github-markup (~> 1.7.0)
gitlab-labkit (~> 0.3.0)
gitlab-markup (~> 1.7.0)
@@ -1134,6 +1143,7 @@ DEPENDENCIES
lograge (~> 0.5)
loofah (~> 2.2)
mail_room (~> 0.9.1)
+ mdl (~> 0.5.0)
memory_profiler (~> 0.9)
method_source (~> 0.8)
mimemagic (~> 0.3.2)
@@ -1142,7 +1152,7 @@ DEPENDENCIES
mysql2 (~> 0.4.10)
nakayoshi_fork (~> 0.0.4)
net-ldap
- net-ssh (~> 5.0)
+ net-ssh (~> 5.2)
nokogiri (~> 1.10.3)
oauth2 (~> 1.4)
octokit (~> 4.9)
@@ -1173,7 +1183,7 @@ DEPENDENCIES
peek-redis (~> 1.2.0)
pg (~> 1.1)
premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.9.6)
+ prometheus-client-mmap (~> 0.9.8)
pry-byebug (~> 3.5.1)
pry-rails (~> 0.3.4)
puma (~> 3.12)
@@ -1199,7 +1209,7 @@ DEPENDENCIES
redis-rails (~> 5.0.2)
request_store (~> 1.3)
responders (~> 2.0)
- rouge (~> 3.1)
+ rouge (~> 3.5)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.7.0)
diff --git a/app/assets/images/auth_buttons/salesforce_64.png b/app/assets/images/auth_buttons/salesforce_64.png
new file mode 100644
index 00000000000..c8a86a0c515
--- /dev/null
+++ b/app/assets/images/auth_buttons/salesforce_64.png
Binary files differ
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 4f66a5d080c..a649c521405 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -24,6 +24,7 @@ const Api = {
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
+ userCountsPath: '/api/:version/user_counts',
usersPath: '/api/:version/users.json',
userPath: '/api/:version/users/:id',
userStatusPath: '/api/:version/users/:id/status',
@@ -312,6 +313,11 @@ const Api = {
});
},
+ userCounts() {
+ const url = Api.buildUrl(this.userCountsPath);
+ return axios.get(url);
+ },
+
userStatus(id, options) {
const url = Api.buildUrl(this.userStatusPath).replace(':id', encodeURIComponent(id));
return axios.get(url, {
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index f58149c9f7b..d8b0b60c183 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -61,7 +61,7 @@ export default {
<div class="board-blank-state p-3">
<p>
{{
- __('BoardBlankState|Add the following default lists to your Issue Board with one click:')
+ s__('BoardBlankState|Add the following default lists to your Issue Board with one click:')
}}
</p>
<ul class="list-unstyled board-blank-state-list">
@@ -76,7 +76,7 @@ export default {
</ul>
<p>
{{
- __(
+ s__(
'BoardBlankState|Starting out with the default set of lists will get you right on the way to making the most of your board.',
)
}}
@@ -86,10 +86,10 @@ export default {
type="button"
@click.stop="addDefaultLists"
>
- {{ __('BoardBlankState|Add default lists') }}
+ {{ s__('BoardBlankState|Add default lists') }}
</button>
<button class="btn btn-default btn-block" type="button" @click.stop="clearBlankState">
- {{ __("BoardBlankState|Nevermind, I'll use my own") }}
+ {{ s__("BoardBlankState|Nevermind, I'll use my own") }}
</button>
</div>
</template>
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 6b54e8baefb..b1b4b1c5508 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -2,7 +2,6 @@ import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable
import FilteredSearchContainer from '../filtered_search/container';
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
import boardsStore from './stores/boards_store';
-import { isEE } from '~/lib/utils/common_utils';
export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
@@ -10,7 +9,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
page: 'boards',
isGroupDecendent: true,
stateFiltersSelector: '.issues-state-filters',
- isGroup: isEE(),
+ isGroup: IS_EE,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index a020765f335..23b107abefa 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import Vue from 'vue';
+import mountMultipleBoardsSwitcher from 'ee_else_ce/boards/mount_multiple_boards_switcher';
import Flash from '~/flash';
import { __ } from '~/locale';
import './models/label';
@@ -20,7 +21,7 @@ import modalMixin from './mixins/modal_mixins';
import './filters/due_date_filters';
import Board from './components/board';
import BoardSidebar from './components/board_sidebar';
-import initNewListDropdown from './components/new_list_dropdown';
+import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import BoardAddIssuesModal from './components/modal/index.vue';
import '~/vue_shared/vue_resource_interceptor';
import {
@@ -78,13 +79,14 @@ export default () => {
},
},
created() {
- gl.boardService = new BoardService({
+ boardsStore.setEndpoints({
boardsEndpoint: this.boardsEndpoint,
recentBoardsEndpoint: this.recentBoardsEndpoint,
listsEndpoint: this.listsEndpoint,
bulkUpdatePath: this.bulkUpdatePath,
boardId: this.boardId,
});
+ gl.boardService = new BoardService();
boardsStore.rootPath = this.boardsEndpoint;
eventHub.$on('updateTokens', this.updateTokens);
@@ -278,4 +280,6 @@ export default () => {
`,
});
}
+
+ mountMultipleBoardsSwitcher();
};
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index f858b162c6b..9069b35db9a 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -5,7 +5,7 @@
import Vue from 'vue';
import './label';
-import { isEE, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import IssueProject from './project';
import boardsStore from '../stores/boards_store';
@@ -91,13 +91,13 @@ class ListIssue {
addMilestone(milestone) {
const miletoneId = this.milestone ? this.milestone.id : null;
- if (isEE && milestone.id !== miletoneId) {
+ if (IS_EE && milestone.id !== miletoneId) {
this.milestone = new ListMilestone(milestone);
}
}
removeMilestone(removeMilestone) {
- if (isEE && removeMilestone && removeMilestone.id === this.milestone.id) {
+ if (IS_EE && removeMilestone && removeMilestone.id === this.milestone.id) {
this.milestone = {};
}
}
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index cd553d0c4af..7e0ccb9bd2a 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -4,7 +4,7 @@
import { __ } from '~/locale';
import ListLabel from './label';
import ListAssignee from './assignee';
-import { isEE, urlParamsToObject } from '~/lib/utils/common_utils';
+import { urlParamsToObject } from '~/lib/utils/common_utils';
import boardsStore from '../stores/boards_store';
import ListMilestone from './milestone';
@@ -58,7 +58,7 @@ class List {
} else if (obj.user) {
this.assignee = new ListAssignee(obj.user);
this.title = this.assignee.name;
- } else if (isEE && obj.milestone) {
+ } else if (IS_EE && obj.milestone) {
this.milestone = new ListMilestone(obj.milestone);
this.title = this.milestone.title;
}
@@ -85,7 +85,7 @@ class List {
entityType = 'label_id';
} else if (this.assignee) {
entityType = 'assignee_id';
- } else if (isEE && this.milestone) {
+ } else if (IS_EE && this.milestone) {
entityType = 'milestone_id';
}
@@ -205,7 +205,7 @@ class List {
issue.addAssignee(this.assignee);
}
- if (isEE && this.milestone) {
+ if (IS_EE && this.milestone) {
if (listFrom && listFrom.type === 'milestone') {
issue.removeMilestone(listFrom.milestone);
}
diff --git a/app/assets/javascripts/boards/models/milestone.js b/app/assets/javascripts/boards/models/milestone.js
index 6f81d6bc6f8..7201b6e91f5 100644
--- a/app/assets/javascripts/boards/models/milestone.js
+++ b/app/assets/javascripts/boards/models/milestone.js
@@ -1,11 +1,9 @@
-import { isEE } from '~/lib/utils/common_utils';
-
export default class ListMilestone {
constructor(obj) {
this.id = obj.id;
this.title = obj.title;
- if (isEE) {
+ if (IS_EE) {
this.path = obj.path;
this.state = obj.state;
this.webUrl = obj.web_url || obj.webUrl;
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
new file mode 100644
index 00000000000..bdb14a7f2f2
--- /dev/null
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -0,0 +1,2 @@
+// this will be moved from EE to CE as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/53811
+export default () => {};
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 7d463f17ab1..580d04a3649 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -1,106 +1,66 @@
-import axios from '../../lib/utils/axios_utils';
-import { mergeUrlParams } from '../../lib/utils/url_utility';
+/* eslint-disable class-methods-use-this */
-export default class BoardService {
- constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
- this.boardsEndpoint = boardsEndpoint;
- this.boardId = boardId;
- this.listsEndpoint = listsEndpoint;
- this.listsEndpointGenerate = `${listsEndpoint}/generate.json`;
- this.bulkUpdatePath = bulkUpdatePath;
- this.recentBoardsEndpoint = `${recentBoardsEndpoint}.json`;
- }
+import boardsStore from '~/boards/stores/boards_store';
+export default class BoardService {
generateBoardsPath(id) {
- return `${this.boardsEndpoint}${id ? `/${id}` : ''}.json`;
+ return boardsStore.generateBoardsPath(id);
}
generateIssuesPath(id) {
- return `${this.listsEndpoint}${id ? `/${id}` : ''}/issues`;
+ return boardsStore.generateIssuesPath(id);
}
static generateIssuePath(boardId, id) {
- return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
- id ? `/${id}` : ''
- }`;
+ return boardsStore.generateIssuePath(boardId, id);
}
all() {
- return axios.get(this.listsEndpoint);
+ return boardsStore.all();
}
generateDefaultLists() {
- return axios.post(this.listsEndpointGenerate, {});
+ return boardsStore.generateDefaultLists();
}
createList(entityId, entityType) {
- const list = {
- [entityType]: entityId,
- };
-
- return axios.post(this.listsEndpoint, {
- list,
- });
+ return boardsStore.createList(entityId, entityType);
}
updateList(id, position) {
- return axios.put(`${this.listsEndpoint}/${id}`, {
- list: {
- position,
- },
- });
+ return boardsStore.updateList(id, position);
}
destroyList(id) {
- return axios.delete(`${this.listsEndpoint}/${id}`);
+ return boardsStore.destroyList(id);
}
getIssuesForList(id, filter = {}) {
- const data = { id };
- Object.keys(filter).forEach(key => {
- data[key] = filter[key];
- });
-
- return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
+ return boardsStore.getIssuesForList(id, filter);
}
moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
- return axios.put(BoardService.generateIssuePath(this.boardId, id), {
- from_list_id: fromListId,
- to_list_id: toListId,
- move_before_id: moveBeforeId,
- move_after_id: moveAfterId,
- });
+ return boardsStore.moveIssue(id, fromListId, toListId, moveBeforeId, moveAfterId);
}
newIssue(id, issue) {
- return axios.post(this.generateIssuesPath(id), {
- issue,
- });
+ return boardsStore.newIssue(id, issue);
}
getBacklog(data) {
- return axios.get(
- mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
- );
+ return boardsStore.getBacklog(data);
}
bulkUpdate(issueIds, extraData = {}) {
- const data = {
- update: Object.assign(extraData, {
- issuable_ids: issueIds.join(','),
- }),
- };
-
- return axios.post(this.bulkUpdatePath, data);
+ return boardsStore.bulkUpdate(issueIds, extraData);
}
static getIssueInfo(endpoint) {
- return axios.get(endpoint);
+ return boardsStore.getIssueInfo(endpoint);
}
static toggleIssueSubscription(endpoint) {
- return axios.post(endpoint);
+ return boardsStore.toggleIssueSubscription(endpoint);
}
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 4ba4cde6bae..b9cd4a143ef 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -8,6 +8,8 @@ import Cookies from 'js-cookie';
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
import { getUrlParamsArray, parseBoolean } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
import eventHub from '../eventhub';
const boardsStore = {
@@ -28,6 +30,7 @@ const boardsStore = {
},
currentPage: '',
reload: false,
+ endpoints: {},
},
detail: {
issue: {},
@@ -36,6 +39,19 @@ const boardsStore = {
issue: {},
list: {},
},
+
+ setEndpoints({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
+ const listsEndpointGenerate = `${listsEndpoint}/generate.json`;
+ this.state.endpoints = {
+ boardsEndpoint,
+ boardId,
+ listsEndpoint,
+ listsEndpointGenerate,
+ bulkUpdatePath,
+ recentBoardsEndpoint: `${recentBoardsEndpoint}.json`,
+ };
+ },
+
create() {
this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&');
@@ -229,6 +245,101 @@ const boardsStore = {
setTimeTrackingLimitToHours(limitToHours) {
this.timeTracking.limitToHours = parseBoolean(limitToHours);
},
+
+ generateBoardsPath(id) {
+ return `${this.state.endpoints.boardsEndpoint}${id ? `/${id}` : ''}.json`;
+ },
+
+ generateIssuesPath(id) {
+ return `${this.state.endpoints.listsEndpoint}${id ? `/${id}` : ''}/issues`;
+ },
+
+ generateIssuePath(boardId, id) {
+ return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
+ id ? `/${id}` : ''
+ }`;
+ },
+
+ all() {
+ return axios.get(this.state.endpoints.listsEndpoint);
+ },
+
+ generateDefaultLists() {
+ return axios.post(this.state.endpoints.listsEndpointGenerate, {});
+ },
+
+ createList(entityId, entityType) {
+ const list = {
+ [entityType]: entityId,
+ };
+
+ return axios.post(this.state.endpoints.listsEndpoint, {
+ list,
+ });
+ },
+
+ updateList(id, position) {
+ return axios.put(`${this.state.endpoints.listsEndpoint}/${id}`, {
+ list: {
+ position,
+ },
+ });
+ },
+
+ destroyList(id) {
+ return axios.delete(`${this.state.endpoints.listsEndpoint}/${id}`);
+ },
+
+ getIssuesForList(id, filter = {}) {
+ const data = { id };
+ Object.keys(filter).forEach(key => {
+ data[key] = filter[key];
+ });
+
+ return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
+ },
+
+ moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
+ return axios.put(this.generateIssuePath(this.state.endpoints.boardId, id), {
+ from_list_id: fromListId,
+ to_list_id: toListId,
+ move_before_id: moveBeforeId,
+ move_after_id: moveAfterId,
+ });
+ },
+
+ newIssue(id, issue) {
+ return axios.post(this.generateIssuesPath(id), {
+ issue,
+ });
+ },
+
+ getBacklog(data) {
+ return axios.get(
+ mergeUrlParams(
+ data,
+ `${gon.relative_url_root}/-/boards/${this.state.endpoints.boardId}/issues.json`,
+ ),
+ );
+ },
+
+ bulkUpdate(issueIds, extraData = {}) {
+ const data = {
+ update: Object.assign(extraData, {
+ issuable_ids: issueIds.join(','),
+ }),
+ };
+
+ return axios.post(this.state.endpoints.bulkUpdatePath, data);
+ },
+
+ getIssueInfo(endpoint) {
+ return axios.get(endpoint);
+ },
+
+ toggleIssueSubscription(endpoint) {
+ return axios.post(endpoint);
+ },
};
BoardsStoreEE.initEESpecific(boardsStore);
diff --git a/app/assets/javascripts/branches/divergence_graph.js b/app/assets/javascripts/branches/divergence_graph.js
index 96bc6a5f8e8..7dbaf984acf 100644
--- a/app/assets/javascripts/branches/divergence_graph.js
+++ b/app/assets/javascripts/branches/divergence_graph.js
@@ -36,7 +36,9 @@ export default endpoint => {
}, 100);
Object.entries(data).forEach(([branchName, val]) => {
- const el = document.querySelector(`.js-branch-${branchName} .js-branch-divergence-graph`);
+ const el = document.querySelector(
+ `[data-name="${branchName}"] .js-branch-divergence-graph`,
+ );
if (!el) return;
diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js
index 0d2fe2925d8..ad0f6cc1496 100644
--- a/app/assets/javascripts/commons/index.js
+++ b/app/assets/javascripts/commons/index.js
@@ -4,3 +4,6 @@ import './jquery';
import './bootstrap';
import './vue';
import '../lib/utils/axios_utils';
+import { openUserCountsBroadcast } from './nav/user_merge_requests';
+
+openUserCountsBroadcast();
diff --git a/app/assets/javascripts/commons/nav/user_merge_requests.js b/app/assets/javascripts/commons/nav/user_merge_requests.js
new file mode 100644
index 00000000000..8e694cca6a1
--- /dev/null
+++ b/app/assets/javascripts/commons/nav/user_merge_requests.js
@@ -0,0 +1,67 @@
+import Api from '~/api';
+
+let channel;
+
+function broadcastCount(newCount) {
+ if (!channel) {
+ return;
+ }
+
+ channel.postMessage(newCount);
+}
+
+function updateUserMergeRequestCounts(newCount) {
+ const mergeRequestsCountEl = document.querySelector('.merge-requests-count');
+ mergeRequestsCountEl.textContent = newCount.toLocaleString();
+ mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0);
+}
+
+/**
+ * Refresh user counts (and broadcast if open)
+ */
+export function refreshUserMergeRequestCounts() {
+ return Api.userCounts()
+ .then(({ data }) => {
+ const count = data.merge_requests;
+
+ updateUserMergeRequestCounts(count);
+ broadcastCount(count);
+ })
+ .catch(ex => {
+ console.error(ex); // eslint-disable-line no-console
+ });
+}
+
+/**
+ * Close the broadcast channel for user counts
+ */
+export function closeUserCountsBroadcast() {
+ if (!channel) {
+ return;
+ }
+
+ channel.close();
+ channel = null;
+}
+
+/**
+ * Open the broadcast channel for user counts, adds user id so we only update
+ *
+ * **Please note:**
+ * Not supported in all browsers, but not polyfilling for now
+ * to keep bundle size small and
+ * no special functionality lost except cross tab notifications
+ */
+export function openUserCountsBroadcast() {
+ closeUserCountsBroadcast();
+
+ if (window.BroadcastChannel) {
+ const currentUserId = typeof gon !== 'undefined' && gon && gon.current_user_id;
+ if (currentUserId) {
+ channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`);
+ channel.onmessage = ev => {
+ updateUserMergeRequestCounts(ev.data);
+ };
+ }
+ }
+}
diff --git a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
new file mode 100644
index 00000000000..444640980af
--- /dev/null
+++ b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
@@ -0,0 +1,58 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { __ } from '~/locale';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ Icon,
+ },
+ props: {
+ projects: {
+ type: Array,
+ required: true,
+ },
+ selectedProject: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ dropdownText() {
+ if (Object.keys(this.selectedProject).length) {
+ return this.selectedProject.name;
+ }
+
+ return __('Select private project');
+ },
+ },
+ methods: {
+ selectProject(project) {
+ this.$emit('click', project);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown toggle-class="d-flex align-items-center w-100" class="w-100">
+ <template slot="button-content">
+ <span class="str-truncated-100 mr-2">
+ <icon name="lock" />
+ {{ dropdownText }}
+ </span>
+ <icon name="chevron-down" class="ml-auto" />
+ </template>
+ <gl-dropdown-item v-for="project in projects" :key="project.id" @click="selectProject(project)">
+ <icon
+ name="mobile-issue-close"
+ :class="{ icon: project.id !== selectedProject.id }"
+ class="js-active-project-check"
+ />
+ <span class="ml-1">{{ project.name }}</span>
+ </gl-dropdown-item>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
new file mode 100644
index 00000000000..99d77a75c23
--- /dev/null
+++ b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
@@ -0,0 +1,132 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import { __, sprintf } from '../../locale';
+import createFlash from '../../flash';
+import Api from '../../api';
+import state from '../state';
+import Dropdown from './dropdown.vue';
+
+export default {
+ components: {
+ GlLink,
+ Dropdown,
+ },
+ props: {
+ namespacePath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ newForkPath: {
+ type: String,
+ required: true,
+ },
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ projects: [],
+ };
+ },
+ computed: {
+ selectedProject() {
+ return state.selectedProject;
+ },
+ noForkText() {
+ return sprintf(
+ __(
+ "To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.",
+ ),
+ { link_start: `<a href="${this.newForkPath}" class="help-link">`, link_end: '</a>' },
+ false,
+ );
+ },
+ },
+ mounted() {
+ this.fetchProjects();
+ this.createBtn = document.querySelector('.js-create-target');
+ this.warningText = document.querySelector('.js-exposed-info-warning');
+ },
+ methods: {
+ selectProject(project) {
+ if (project) {
+ Object.assign(state, {
+ selectedProject: project,
+ });
+
+ if (project.namespaceFullPath !== this.namespacePath) {
+ this.showWarning();
+ }
+ } else if (this.createBtn) {
+ this.createBtn.setAttribute('disabled', 'disabled');
+ }
+ },
+ normalizeProjectData(data) {
+ return data.map(p => ({
+ id: p.id,
+ name: p.name_with_namespace,
+ pathWithNamespace: p.path_with_namespace,
+ namespaceFullpath: p.namespace.full_path,
+ }));
+ },
+ fetchProjects() {
+ Api.projectForks(this.projectPath, {
+ with_merge_requests_enabled: true,
+ min_access_level: 30,
+ visibility: 'private',
+ })
+ .then(({ data }) => {
+ this.projects = this.normalizeProjectData(data);
+ this.selectProject(this.projects[0]);
+ })
+ .catch(e => {
+ createFlash(__('Error fetching forked projects. Please try again.'));
+ throw e;
+ });
+ },
+ showWarning() {
+ if (this.warningText) {
+ this.warningText.classList.remove('hidden');
+ }
+
+ if (this.createBtn) {
+ this.createBtn.classList.add('btn-warning');
+ this.createBtn.classList.remove('btn-success');
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="form-group">
+ <label>{{ __('Project') }}</label>
+ <div>
+ <dropdown
+ v-if="projects.length"
+ :projects="projects"
+ :selected-project="selectedProject"
+ @click="selectProject"
+ />
+ <p class="text-muted mt-1 mb-0">
+ <template v-if="projects.length">
+ {{
+ __(
+ "To protect this issue's confidentiality, a private fork of this project was selected.",
+ )
+ }}
+ </template>
+ <template v-else>
+ {{ __('No forks available to you.') }}<br />
+ <span v-html="noForkText"></span>
+ </template>
+ </p>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/confidential_merge_request/index.js b/app/assets/javascripts/confidential_merge_request/index.js
new file mode 100644
index 00000000000..9672821d30e
--- /dev/null
+++ b/app/assets/javascripts/confidential_merge_request/index.js
@@ -0,0 +1,30 @@
+import Vue from 'vue';
+import { parseBoolean } from '../lib/utils/common_utils';
+import ProjectFormGroup from './components/project_form_group.vue';
+import state from './state';
+
+export function isConfidentialIssue() {
+ return parseBoolean(document.querySelector('.js-create-mr').dataset.isConfidential);
+}
+
+export function canCreateConfidentialMergeRequest() {
+ return isConfidentialIssue() && Object.keys(state.selectedProject).length > 0;
+}
+
+export function init() {
+ const el = document.getElementById('js-forked-project');
+
+ return new Vue({
+ el,
+ render(h) {
+ return h(ProjectFormGroup, {
+ props: {
+ namespacePath: el.dataset.namespacePath,
+ projectPath: el.dataset.projectPath,
+ newForkPath: el.dataset.newForkPath,
+ helpPagePath: el.dataset.helpPagePath,
+ },
+ });
+ },
+ });
+}
diff --git a/app/assets/javascripts/confidential_merge_request/state.js b/app/assets/javascripts/confidential_merge_request/state.js
new file mode 100644
index 00000000000..95b0580f4b9
--- /dev/null
+++ b/app/assets/javascripts/confidential_merge_request/state.js
@@ -0,0 +1,5 @@
+import Vue from 'vue';
+
+export default Vue.observable({
+ selectedProject: {},
+});
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index 8f5cece0788..052168bb21c 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -5,6 +5,12 @@ import Flash from './flash';
import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
import { __, sprintf } from './locale';
+import {
+ init as initConfidentialMergeRequest,
+ isConfidentialIssue,
+ canCreateConfidentialMergeRequest,
+} from './confidential_merge_request';
+import confidentialMergeRequestState from './confidential_merge_request/state';
// Todo: Remove this when fixing issue in input_setter plugin
const InputSetter = Object.assign({}, ISetter);
@@ -12,6 +18,17 @@ const InputSetter = Object.assign({}, ISetter);
const CREATE_MERGE_REQUEST = 'create-mr';
const CREATE_BRANCH = 'create-branch';
+function createEndpoint(projectPath, endpoint) {
+ if (canCreateConfidentialMergeRequest()) {
+ return endpoint.replace(
+ projectPath,
+ confidentialMergeRequestState.selectedProject.pathWithNamespace,
+ );
+ }
+
+ return endpoint;
+}
+
export default class CreateMergeRequestDropdown {
constructor(wrapperEl) {
this.wrapperEl = wrapperEl;
@@ -42,6 +59,8 @@ export default class CreateMergeRequestDropdown {
this.refIsValid = true;
this.refsPath = this.wrapperEl.dataset.refsPath;
this.suggestedRef = this.refInput.value;
+ this.projectPath = this.wrapperEl.dataset.projectPath;
+ this.projectId = this.wrapperEl.dataset.projectId;
// These regexps are used to replace
// a backend generated new branch name and its source (ref)
@@ -58,6 +77,14 @@ export default class CreateMergeRequestDropdown {
};
this.init();
+
+ if (isConfidentialIssue()) {
+ this.createMergeRequestButton.setAttribute(
+ 'data-dropdown-trigger',
+ '#create-merge-request-dropdown',
+ );
+ initConfidentialMergeRequest();
+ }
}
available() {
@@ -113,7 +140,9 @@ export default class CreateMergeRequestDropdown {
this.isCreatingBranch = true;
return axios
- .post(this.createBranchPath)
+ .post(createEndpoint(this.projectPath, this.createBranchPath), {
+ confidential_issue_project_id: canCreateConfidentialMergeRequest() ? this.projectId : null,
+ })
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
@@ -125,7 +154,11 @@ export default class CreateMergeRequestDropdown {
this.isCreatingMergeRequest = true;
return axios
- .post(this.createMrPath)
+ .post(this.createMrPath, {
+ target_project_id: canCreateConfidentialMergeRequest()
+ ? confidentialMergeRequestState.selectedProject.id
+ : null,
+ })
.then(({ data }) => {
this.mergeRequestCreated = true;
window.location.href = data.url;
@@ -149,6 +182,8 @@ export default class CreateMergeRequestDropdown {
}
enable() {
+ if (!canCreateConfidentialMergeRequest()) return;
+
this.createMergeRequestButton.classList.remove('disabled');
this.createMergeRequestButton.removeAttribute('disabled');
@@ -205,7 +240,7 @@ export default class CreateMergeRequestDropdown {
if (!ref) return false;
return axios
- .get(`${this.refsPath}${encodeURIComponent(ref)}`)
+ .get(`${createEndpoint(this.projectPath, this.refsPath)}${encodeURIComponent(ref)}`)
.then(({ data }) => {
const branches = data[Object.keys(data)[0]];
const tags = data[Object.keys(data)[1]];
@@ -325,6 +360,12 @@ export default class CreateMergeRequestDropdown {
let xhr = null;
event.preventDefault();
+ if (isConfidentialIssue() && !event.target.classList.contains('js-create-target')) {
+ this.droplab.hooks.forEach(hook => hook.list.toggle());
+
+ return;
+ }
+
if (this.isBusy()) {
return;
}
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index ca3285e9afd..a06dbd70ac5 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -24,6 +24,11 @@ export default {
required: false,
default: '',
},
+ hasDraft: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
className() {
@@ -55,6 +60,7 @@ export default {
:help-page-path="helpPagePath"
/>
<diff-discussion-reply
+ v-if="!hasDraft"
:has-form="line.hasForm"
:render-reply-placeholder="Boolean(line.discussions.length)"
@showNewDiscussionForm="
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 8c76a555b62..b2bc3d9914f 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -57,6 +57,7 @@ export default {
:diff-file-hash="diffFile.file_hash"
:line="line"
:help-page-path="helpPagePath"
+ :has-draft="shouldRenderDraftRow(diffFile.file_hash, line) || false"
/>
<inline-draft-comment-row
v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index c00b0e010ff..65b41b0e456 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -28,6 +28,16 @@ export default {
required: false,
default: '',
},
+ hasDraftLeft: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ hasDraftRight: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
hasExpandedDiscussionOnLeft() {
@@ -121,6 +131,7 @@ export default {
/>
</div>
<diff-discussion-reply
+ v-if="!hasDraftLeft"
:has-form="showLeftSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnLeft"
@showNewDiscussionForm="showNewDiscussionForm"
@@ -145,6 +156,7 @@ export default {
/>
</div>
<diff-discussion-reply
+ v-if="!hasDraftRight"
:has-form="showRightSideCommentForm"
:render-reply-placeholder="shouldRenderReplyPlaceholderOnRight"
@showNewDiscussionForm="showNewDiscussionForm"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 41a80d99850..c477e68c33c 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -58,6 +58,8 @@ export default {
:diff-file-hash="diffFile.file_hash"
:line-index="index"
:help-page-path="helpPagePath"
+ :has-draft-left="hasParallelDraftLeft(diffFile.file_hash, line) || false"
+ :has-draft-right="hasParallelDraftRight(diffFile.file_hash, line) || false"
/>
<parallel-draft-comment-row
v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)"
diff --git a/app/assets/javascripts/diffs/mixins/draft_comments.js b/app/assets/javascripts/diffs/mixins/draft_comments.js
index dfb71bf38ce..b6c9b132aeb 100644
--- a/app/assets/javascripts/diffs/mixins/draft_comments.js
+++ b/app/assets/javascripts/diffs/mixins/draft_comments.js
@@ -6,5 +6,7 @@ export default {
imageDiscussions() {
return this.diffFile.discussions;
},
+ hasParallelDraftLeft: () => () => false,
+ hasParallelDraftRight: () => () => false,
},
};
diff --git a/app/assets/javascripts/event_tracking/notes.js b/app/assets/javascripts/event_tracking/notes.js
index 2d1ec238274..1f70290c397 100644
--- a/app/assets/javascripts/event_tracking/notes.js
+++ b/app/assets/javascripts/event_tracking/notes.js
@@ -1 +1,2 @@
+// Noop function which has a EE counter-part
export default () => {};
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 5fcb11a232e..03756a634d5 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -144,7 +144,9 @@ export default {
'triggerFilesChange',
]),
initEditor() {
- if (this.shouldHideEditor) return;
+ if (this.shouldHideEditor && (this.file.content || this.file.raw)) {
+ return;
+ }
this.editor.clearEditor();
diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js
index b8abaa41f23..51278640b5b 100644
--- a/app/assets/javascripts/ide/lib/files.js
+++ b/app/assets/javascripts/ide/lib/files.js
@@ -77,6 +77,7 @@ export const decorateFiles = ({
const fileFolder = parent && insertParent(parent);
if (name) {
+ const previewMode = viewerInformationForPath(name);
parentPath = fileFolder && fileFolder.path;
file = decorateData({
@@ -92,9 +93,9 @@ export const decorateFiles = ({
changed: tempFile,
content,
base64,
- binary,
+ binary: (previewMode && previewMode.binary) || binary,
rawPath,
- previewMode: viewerInformationForPath(name),
+ previewMode,
parentPath,
});
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index c88244492e0..a52f1e235ed 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,6 +1,7 @@
import * as types from '../mutation_types';
import { sortTree } from '../utils';
import { diffModes } from '../../constants';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default {
[types.SET_FILE_ACTIVE](state, { path, active }) {
@@ -35,19 +36,18 @@ export default {
}
},
[types.SET_FILE_DATA](state, { data, file }) {
- Object.assign(state.entries[file.path], {
- id: data.id,
- blamePath: data.blame_path,
- commitsPath: data.commits_path,
- permalink: data.permalink,
- rawPath: data.raw_path,
- binary: data.binary,
- renderError: data.render_error,
- raw: (state.entries[file.path] && state.entries[file.path].raw) || null,
- baseRaw: null,
- html: data.html,
- size: data.size,
- lastCommitSha: data.last_commit_sha,
+ const stateEntry = state.entries[file.path];
+ const stagedFile = state.stagedFiles.find(f => f.path === file.path);
+ const openFile = state.openFiles.find(f => f.path === file.path);
+ const changedFile = state.changedFiles.find(f => f.path === file.path);
+
+ [stateEntry, stagedFile, openFile, changedFile].forEach(f => {
+ if (f) {
+ Object.assign(f, convertObjectPropsToCamelCase(data, { dropKeys: ['raw', 'baseRaw'] }), {
+ raw: (stateEntry && stateEntry.raw) || null,
+ baseRaw: null,
+ });
+ }
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js
index bc9d7fcf30d..c855f3973b0 100644
--- a/app/assets/javascripts/issuable_bulk_update_actions.js
+++ b/app/assets/javascripts/issuable_bulk_update_actions.js
@@ -1,4 +1,4 @@
-/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback, no-unused-vars */
+/* eslint-disable consistent-return, func-names, array-callback-return, prefer-arrow-callback */
import $ from 'jquery';
import _ from 'underscore';
@@ -7,7 +7,7 @@ import Flash from './flash';
import { __ } from './locale';
export default {
- init({ container, form, issues, prefixId } = {}) {
+ init({ form, issues, prefixId } = {}) {
this.prefixId = prefixId || 'issue_';
this.form = form || this.getElement('.bulk-update');
this.$labelDropdown = this.form.find('.js-label-select');
diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js
index 16f88cddce3..f3f8b6ec715 100644
--- a/app/assets/javascripts/issuable_index.js
+++ b/app/assets/javascripts/issuable_index.js
@@ -2,26 +2,13 @@ import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import flash from './flash';
import { s__, __ } from './locale';
-import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
-import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
+import issuableInitBulkUpdateSidebar from './issuable_init_bulk_update_sidebar';
export default class IssuableIndex {
constructor(pagePrefix) {
- this.initBulkUpdate(pagePrefix);
+ issuableInitBulkUpdateSidebar.init(pagePrefix);
IssuableIndex.resetIncomingEmailToken();
}
- initBulkUpdate(pagePrefix) {
- const userCanBulkUpdate = $('.issues-bulk-update').length > 0;
- const alreadyInitialized = Boolean(this.bulkUpdateSidebar);
-
- if (userCanBulkUpdate && !alreadyInitialized) {
- IssuableBulkUpdateActions.init({
- prefixId: pagePrefix,
- });
-
- this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar();
- }
- }
static resetIncomingEmailToken() {
const $resetToken = $('.incoming-email-token-reset');
diff --git a/app/assets/javascripts/issuable_init_bulk_update_sidebar.js b/app/assets/javascripts/issuable_init_bulk_update_sidebar.js
new file mode 100644
index 00000000000..da8969c80f3
--- /dev/null
+++ b/app/assets/javascripts/issuable_init_bulk_update_sidebar.js
@@ -0,0 +1,19 @@
+import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
+import issuableBulkUpdateActions from './issuable_bulk_update_actions';
+
+export default {
+ bulkUpdateSidebar: null,
+
+ init(prefixId) {
+ const bulkUpdateEl = document.querySelector('.issues-bulk-update');
+ const alreadyInitialized = Boolean(this.bulkUpdateSidebar);
+
+ if (bulkUpdateEl && !alreadyInitialized) {
+ issuableBulkUpdateActions.init({ prefixId });
+
+ this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar();
+ }
+
+ return this.bulkUpdateSidebar;
+ },
+};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 3f954b43ee3..bea43430edc 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -11,7 +11,7 @@ import CreateLabelDropdown from './create_label';
import flash from './flash';
import ModalStore from './boards/stores/modal_store';
import boardsStore from './boards/stores/boards_store';
-import { isEE, isScopedLabel } from '~/lib/utils/common_utils';
+import { isScopedLabel } from '~/lib/utils/common_utils';
export default class LabelsSelect {
constructor(els, options = {}) {
@@ -140,7 +140,7 @@ export default class LabelsSelect {
labelCount = data.labels.length;
// EE Specific
- if (isEE) {
+ if (IS_EE) {
/**
* For Scoped labels, the last label selected with the
* same key will be applied to the current issueable.
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index cc5e12aa467..5e90893b684 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -727,14 +727,6 @@ export const NavigationType = {
};
/**
- * Returns the value of `gon.ee`
- * Used to check if it's the EE codebase or the CE one.
- *
- * @returns Boolean
- */
-export const isEE = () => window.gon && window.gon.ee;
-
-/**
* Checks if the given Label has a special syntax `::` in
* it's title.
*
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 9f30a989295..9e97f345717 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -33,6 +33,8 @@ import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import { __ } from './locale';
+import 'ee_else_ce/main_ee';
+
// expose jQuery as global (TODO: remove these)
window.jQuery = jQuery;
window.$ = jQuery;
@@ -119,11 +121,15 @@ function deferredInitialisation() {
.catch(() => {});
}
+ const glTooltipDelay = localStorage.getItem('gl-tooltip-delay');
+ const delay = glTooltipDelay ? JSON.parse(glTooltipDelay) : 0;
+
// Initialize tooltips
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
trigger: 'hover',
boundary: 'viewport',
+ delay,
});
// Initialize popovers
diff --git a/app/assets/javascripts/main_ee.js b/app/assets/javascripts/main_ee.js
new file mode 100644
index 00000000000..84d74775163
--- /dev/null
+++ b/app/assets/javascripts/main_ee.js
@@ -0,0 +1 @@
+// This is an empty file to satisfy ee_else_ce import for the EE main entry point
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 2cbda8ea05d..ba79a697df2 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -4,7 +4,6 @@ import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import '~/vue_shared/mixins/is_ee';
import { getParameterValues } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url';
import MonitorAreaChart from './charts/area.vue';
@@ -124,6 +123,11 @@ export default {
required: false,
default: '',
},
+ smallEmptyState: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -155,6 +159,12 @@ export default {
selectedDashboardText() {
return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name);
},
+ addingMetricsAvailable() {
+ return IS_EE && this.canAddMetrics && !this.showEmptyState;
+ },
+ alertWidgetAvailable() {
+ return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint;
+ },
},
created() {
this.setEndpoints({
@@ -308,7 +318,7 @@ export default {
</div>
</div>
<div class="d-flex">
- <div v-if="isEE && canAddMetrics && !showEmptyState">
+ <div v-if="addingMetricsAvailable">
<gl-button
v-gl-modal-directive="$options.addMetric.modalId"
class="js-add-metric-button text-success border-success"
@@ -367,7 +377,7 @@ export default {
group-id="monitor-area-chart"
>
<alert-widget
- v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData"
+ v-if="alertWidgetAvailable && graphData"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@@ -386,6 +396,7 @@ export default {
:empty-loading-svg-path="emptyLoadingSvgPath"
:empty-no-data-svg-path="emptyNoDataSvgPath"
:empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
+ :compact="smallEmptyState"
/>
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue
index a3c6de14aa4..1bb40447a3e 100644
--- a/app/assets/javascripts/monitoring/components/empty_state.vue
+++ b/app/assets/javascripts/monitoring/components/empty_state.vue
@@ -1,7 +1,11 @@
<script>
import { __ } from '~/locale';
+import { GlEmptyState } from '@gitlab/ui';
export default {
+ components: {
+ GlEmptyState,
+ },
props: {
documentationPath: {
type: String,
@@ -37,6 +41,11 @@ export default {
type: String,
required: true,
},
+ compact: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -58,6 +67,8 @@ export default {
If this takes a long time, ensure that data is available.`),
buttonText: __('View documentation'),
buttonPath: this.documentationPath,
+ secondaryButtonText: '',
+ secondaryButtonPath: '',
},
noData: {
svgUrl: this.emptyNoDataSvgPath,
@@ -66,13 +77,19 @@ export default {
no data to display.`),
buttonText: __('Configure Prometheus'),
buttonPath: this.settingsPath,
+ secondaryButtonText: '',
+ secondaryButtonPath: '',
},
unableToConnect: {
svgUrl: this.emptyUnableToConnectSvgPath,
title: __('Unable to connect to Prometheus server'),
- description: 'Ensure connectivity is available from the GitLab server to the ',
+ description: __(
+ 'Ensure connectivity is available from the GitLab server to the Prometheus server',
+ ),
buttonText: __('View documentation'),
buttonPath: this.documentationPath,
+ secondaryButtonText: __('Configure Prometheus'),
+ secondaryButtonPath: this.settingsPath,
},
},
};
@@ -81,45 +98,19 @@ export default {
currentState() {
return this.states[this.selectedState];
},
- showButtonDescription() {
- if (this.selectedState === 'unableToConnect') return true;
- return false;
- },
},
};
</script>
<template>
- <div class="row empty-state js-empty-state">
- <div class="col-12">
- <div class="state-svg svg-content">
- <img :src="currentState.svgUrl" />
- </div>
- </div>
-
- <div class="col-12">
- <div class="text-content">
- <h4 class="state-title text-center">{{ currentState.title }}</h4>
- <p class="state-description">
- {{ currentState.description }}
- <a v-if="showButtonDescription" :href="settingsPath">{{ __('Prometheus server') }}</a>
- </p>
-
- <div class="text-center">
- <a
- v-if="currentState.buttonPath"
- :href="currentState.buttonPath"
- class="btn btn-success"
- >{{ currentState.buttonText }}</a
- >
- <a
- v-if="currentState.secondaryButtonPath"
- :href="currentState.secondaryButtonPath"
- class="btn"
- >{{ currentState.secondaryButtonText }}</a
- >
- </div>
- </div>
- </div>
- </div>
+ <gl-empty-state
+ :title="currentState.title"
+ :description="currentState.description"
+ :primary-button-text="currentState.buttonText"
+ :primary-button-link="currentState.buttonPath"
+ :secondary-button-text="currentState.secondaryButtonText"
+ :secondary-button-link="currentState.secondaryButtonPath"
+ :svg-path="currentState.svgUrl"
+ :compact="compact"
+ />
</template>
diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js
index 84e1f1c4c20..721942f9d3b 100644
--- a/app/assets/javascripts/monitoring/stores/utils.js
+++ b/app/assets/javascripts/monitoring/stores/utils.js
@@ -36,15 +36,26 @@ function removeTimeSeriesNoData(queries) {
// { metricId: 2, ...query2Attrs }] },
// { title: 'new title', y_label: 'MB', queries: [{ metricId: 3, ...query3Attrs }]}
// ]
-function groupQueriesByChartInfo(metrics) {
+export function groupQueriesByChartInfo(metrics) {
const metricsByChart = metrics.reduce((accumulator, metric) => {
const { queries, ...chart } = metric;
- const metricId = chart.id ? chart.id.toString() : null;
const chartKey = `${chart.title}|${chart.y_label}`;
accumulator[chartKey] = accumulator[chartKey] || { ...chart, queries: [] };
- queries.forEach(queryAttrs => accumulator[chartKey].queries.push({ metricId, ...queryAttrs }));
+ queries.forEach(queryAttrs => {
+ let metricId;
+
+ if (chart.id) {
+ metricId = chart.id.toString();
+ } else if (queryAttrs.metric_id) {
+ metricId = queryAttrs.metric_id.toString();
+ } else {
+ metricId = null;
+ }
+
+ accumulator[chartKey].queries.push({ metricId, ...queryAttrs });
+ });
return accumulator;
}, {});
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 5a4b5f9398b..fda494fec07 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -13,6 +13,7 @@ import {
splitCamelCase,
slugifyWithUnderscore,
} from '../../lib/utils/text_utility';
+import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import * as constants from '../constants';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
@@ -65,14 +66,12 @@ export default {
return this.getUserData.id;
},
commentButtonTitle() {
- return this.noteType === constants.COMMENT ? 'Comment' : 'Start thread';
+ return this.noteType === constants.COMMENT ? __('Comment') : __('Start thread');
},
startDiscussionDescription() {
- let text = 'Discuss a specific suggestion or question';
- if (this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE) {
- text += ' that needs to be resolved';
- }
- return `${text}.`;
+ return this.getNoteableData.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
+ ? __('Discuss a specific suggestion or question that needs to be resolved.')
+ : __('Discuss a specific suggestion or question.');
},
isOpen() {
return this.openState === constants.OPENED || this.openState === constants.REOPENED;
@@ -127,8 +126,8 @@ export default {
},
issuableTypeTitle() {
return this.noteableType === constants.MERGE_REQUEST_NOTEABLE_TYPE
- ? 'merge request'
- : 'issue';
+ ? __('merge request')
+ : __('issue');
},
trackingLabel() {
return slugifyWithUnderscore(`${this.commentButtonTitle} button`);
@@ -203,7 +202,7 @@ export default {
this.discard();
} else {
Flash(
- 'Something went wrong while adding your comment. Please try again.',
+ __('Something went wrong while adding your comment. Please try again.'),
'alert',
this.$refs.commentForm,
);
@@ -219,8 +218,9 @@ export default {
.catch(() => {
this.enableButton();
this.discard(false);
- const msg = `Your comment could not be submitted!
-Please check your network connection and try again.`;
+ const msg = __(
+ 'Your comment could not be submitted! Please check your network connection and try again.',
+ );
Flash(msg, 'alert', this.$el);
this.note = noteData.data.note.note; // Restore textarea content.
this.removePlaceholderNotes();
@@ -235,7 +235,10 @@ Please check your network connection and try again.`;
toggleIssueState() {
if (this.isOpen) {
this.closeIssue()
- .then(() => this.enableButton())
+ .then(() => {
+ this.enableButton();
+ refreshUserMergeRequestCounts();
+ })
.catch(() => {
this.enableButton();
this.toggleStateButtonLoading(false);
@@ -248,7 +251,10 @@ Please check your network connection and try again.`;
});
} else {
this.reopenIssue()
- .then(() => this.enableButton())
+ .then(() => {
+ this.enableButton();
+ refreshUserMergeRequestCounts();
+ })
.catch(({ data }) => {
this.enableButton();
this.toggleStateButtonLoading(false);
@@ -298,7 +304,7 @@ Please check your network connection and try again.`;
const noteableType = capitalizeFirstCharacter(convertToCamelCase(this.noteableType));
this.autosave = new Autosave($(this.$refs.textarea), [
- 'Note',
+ __('Note'),
noteableType,
this.getNoteableData.id,
]);
@@ -359,8 +365,8 @@ Please check your network connection and try again.`;
class="note-textarea js-vue-comment-form js-note-text
js-gfm-input js-autosize markdown-area js-vue-textarea qa-comment-input"
data-supports-quick-actions="true"
- aria-label="Description"
- placeholder="Write a comment or drag your files here…"
+ :aria-label="__('Description')"
+ :placeholder="__('Write a comment or drag your files here…')"
@keydown.up="editCurrentUserLastNote()"
@keydown.meta.enter="handleSave()"
@keydown.ctrl.enter="handleSave()"
@@ -381,7 +387,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
data-track-event="click_button"
@click.prevent="handleSave()"
>
- {{ __(commentButtonTitle) }}
+ {{ commentButtonTitle }}
</button>
<button
:disabled="isSubmitButtonDisabled"
@@ -390,7 +396,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
class="btn btn-success note-type-toggle js-note-new-discussion dropdown-toggle qa-note-dropdown"
data-display="static"
data-toggle="dropdown"
- aria-label="Open comment type dropdown"
+ :aria-label="__('Open comment type dropdown')"
>
<i aria-hidden="true" class="fa fa-caret-down toggle-icon"> </i>
</button>
@@ -404,8 +410,14 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
>
<i aria-hidden="true" class="fa fa-check icon"> </i>
<div class="description">
- <strong>Comment</strong>
- <p>Add a general comment to this {{ noteableDisplayName }}.</p>
+ <strong>{{ __('Comment') }}</strong>
+ <p>
+ {{
+ sprintf(__('Add a general comment to this %{noteableDisplayName}.'), {
+ noteableDisplayName,
+ })
+ }}
+ </p>
</div>
</button>
</li>
@@ -418,7 +430,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
>
<i aria-hidden="true" class="fa fa-check icon"> </i>
<div class="description">
- <strong>Start thread</strong>
+ <strong>{{ __('Start thread') }}</strong>
<p>{{ startDiscussionDescription }}</p>
</div>
</button>
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 54c242b2fda..164e79c6294 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -100,7 +100,7 @@ export default {
class="btn-link btn-link-retry btn-no-padding js-toggle-lazy-diff-retry-button"
@click="fetchDiff"
>
- Try again
+ {{ __('Try again') }}
</button>
</td>
<td v-else class="line_content js-success-lazy-load">
diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue
index 2ff0fee62f3..0b136549c14 100644
--- a/app/assets/javascripts/notes/components/discussion_notes.vue
+++ b/app/assets/javascripts/notes/components/discussion_notes.vue
@@ -8,12 +8,14 @@ import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import NoteableNote from './noteable_note.vue';
import ToggleRepliesWidget from './toggle_replies_widget.vue';
import NoteEditedText from './note_edited_text.vue';
+import DiscussionNotesRepliesWrapper from './discussion_notes_replies_wrapper.vue';
export default {
name: 'DiscussionNotes',
components: {
ToggleRepliesWidget,
NoteEditedText,
+ DiscussionNotesRepliesWrapper,
},
props: {
discussion: {
@@ -119,9 +121,7 @@ export default {
/>
<slot slot="avatar-badge" name="avatar-badge"></slot>
</component>
- <div
- :class="discussion.diff_discussion ? 'discussion-collapsible bordered-box clearfix' : ''"
- >
+ <discussion-notes-replies-wrapper :is-diff-discussion="discussion.diff_discussion">
<toggle-replies-widget
v-if="hasReplies"
:collapsed="!isExpanded"
@@ -141,7 +141,7 @@ export default {
/>
</template>
<slot :show-replies="isExpanded || !hasReplies" name="footer"></slot>
- </div>
+ </discussion-notes-replies-wrapper>
</template>
<template v-else>
<component
diff --git a/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue b/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue
new file mode 100644
index 00000000000..2ddca56ddd5
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_notes_replies_wrapper.vue
@@ -0,0 +1,27 @@
+<script>
+/**
+ * Wrapper for discussion notes replies section.
+ *
+ * This is a functional component using the render method because in some cases
+ * the wrapper is not needed and we want to simply render along the children.
+ */
+export default {
+ functional: true,
+ props: {
+ isDiffDiscussion: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ render(h, { props, children }) {
+ if (props.isDiffDiscussion) {
+ return h('li', { class: 'discussion-collapsible bordered-box clearfix' }, [
+ h('ul', { class: 'notes' }, children),
+ ]);
+ }
+
+ return children;
+ },
+};
+</script>
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index 941b6d5cab3..d4a57d5d58d 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -4,6 +4,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
import { glEmojiTag } from '../../emoji';
+import { __, sprintf } from '~/locale';
export default {
components: {
@@ -108,23 +109,26 @@ export default {
// Add myself to the beginning of the list so title will start with You.
if (hasReactionByCurrentUser) {
- namesToShow.unshift('You');
+ namesToShow.unshift(__('You'));
}
let title = '';
// We have 10+ awarded user, join them with comma and add `and x more`.
if (remainingAwardList.length) {
- title = `${namesToShow.join(', ')}, and ${remainingAwardList.length} more.`;
+ title = sprintf(__(`%{listToShow}, and %{awardsListLength} more.`), {
+ listToShow: namesToShow.join(', '),
+ awardsListLength: remainingAwardList.length,
+ });
} else if (namesToShow.length > 1) {
// Join all names with comma but not the last one, it will be added with and text.
title = namesToShow.slice(0, namesToShow.length - 1).join(', ');
// If we have more than 2 users we need an extra comma before and text.
title += namesToShow.length > 2 ? ',' : '';
- title += ` and ${namesToShow.slice(-1)}`; // Append and text
+ title += sprintf(__(` and %{sliced}`), { sliced: namesToShow.slice(-1) }); // Append and text
} else {
// We have only 2 users so join them with and.
- title = namesToShow.join(' and ');
+ title = namesToShow.join(__(' and '));
}
return title;
@@ -155,7 +159,7 @@ export default {
awardName: parsedName,
};
- this.toggleAwardRequest(data).catch(() => Flash('Something went wrong on our end.'));
+ this.toggleAwardRequest(data).catch(() => Flash(__('Something went wrong on our end.')));
},
},
};
@@ -184,7 +188,7 @@ export default {
:class="{ 'js-user-authored': isAuthoredByMe }"
class="award-control btn js-add-award"
title="Add reaction"
- aria-label="Add reaction"
+ :aria-label="__('Add reaction')"
data-boundary="viewport"
type="button"
>
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 01be4f2b094..3823861c0b9 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -1,14 +1,14 @@
<script>
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { mapGetters, mapActions } from 'vuex';
+import noteFormMixin from 'ee_else_ce/notes/mixins/note_form';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
import markdownField from '../../vue_shared/components/markdown/field.vue';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
-import noteFormMixin from 'ee_else_ce/notes/mixins/note_form';
export default {
name: 'NoteForm',
@@ -174,6 +174,18 @@ export default {
(this.line && this.line.can_receive_suggestion)
);
},
+ changedCommentText() {
+ return sprintf(
+ __(
+ 'This comment has changed since you started editing, please review the %{startTag}updated comment%{endTag} to ensure information is not lost.',
+ ),
+ {
+ startTag: `<a href="${this.noteHash}" target="_blank" rel="noopener noreferrer">`,
+ endTag: '</a>',
+ },
+ false,
+ );
+ },
},
watch: {
noteBody() {
@@ -228,11 +240,11 @@ export default {
<template>
<div ref="editNoteForm" class="note-edit-form current-note-edit-form js-discussion-note-form">
- <div v-if="conflictWhileEditing" class="js-conflict-edit-warning alert alert-danger">
- This comment has changed since you started editing, please review the
- <a :href="noteHash" target="_blank" rel="noopener noreferrer">updated comment</a> to ensure
- information is not lost.
- </div>
+ <div
+ v-if="conflictWhileEditing"
+ class="js-conflict-edit-warning alert alert-danger"
+ v-html="changedCommentText"
+ ></div>
<div class="flash-container timeline-content"></div>
<form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form">
<issue-warning
@@ -264,8 +276,8 @@ export default {
name="note[note]"
class="note-textarea js-gfm-input js-note-text js-autosize markdown-area js-vue-issue-note-form js-vue-textarea qa-reply-input"
dir="auto"
- aria-label="Description"
- placeholder="Write a comment or drag your files here…"
+ :aria-label="__('Description')"
+ :placeholder="__('Write a comment or drag your files here…')"
@keydown.meta.enter="handleKeySubmit()"
@keydown.ctrl.enter="handleKeySubmit()"
@keydown.exact.up="editMyLastNote()"
@@ -339,7 +351,7 @@ export default {
type="button"
@click="cancelHandler()"
>
- Cancel
+ {{ __('Cancel') }}
</button>
</template>
</div>
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 6466ab3acbe..3158e086f6c 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -103,7 +103,7 @@ export default {
</template>
<i
class="fa fa-spinner fa-spin editing-spinner"
- aria-label="Comment is being updated"
+ :aria-label="__('Comment is being updated')"
aria-hidden="true"
></i>
</span>
diff --git a/app/assets/javascripts/notes/components/note_signed_out_widget.vue b/app/assets/javascripts/notes/components/note_signed_out_widget.vue
index e3eb92956b1..ccfe84ab098 100644
--- a/app/assets/javascripts/notes/components/note_signed_out_widget.vue
+++ b/app/assets/javascripts/notes/components/note_signed_out_widget.vue
@@ -1,5 +1,6 @@
<script>
import { mapGetters } from 'vuex';
+import { __, sprintf } from '~/locale';
export default {
computed: {
@@ -10,12 +11,24 @@ export default {
signInLink() {
return this.getNotesDataByProp('newSessionPath');
},
+ signedOutText() {
+ return sprintf(
+ __(
+ 'Please %{startTagRegister}register%{endRegisterTag} or %{startTagSignIn}sign in%{endSignInTag} to reply',
+ ),
+ {
+ startTagRegister: `<a href="${this.registerLink}">`,
+ startTagSignIn: `<a href="${this.signInLink}">`,
+ endRegisterTag: '</a>',
+ endSignInTag: '</a>',
+ },
+ false,
+ );
+ },
},
};
</script>
<template>
- <div class="disabled-comment text-center">
- Please <a :href="registerLink">register</a> or <a :href="signInLink">sign in</a> to reply
- </div>
+ <div class="disabled-comment text-center" v-html="signedOutText"></div>
</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index a71a89cfffc..ac743d9f4b8 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -144,15 +144,6 @@ export default {
return {};
},
- componentClassName() {
- if (this.shouldRenderDiffs) {
- if (!this.lastUpdatedAt && !this.discussion.resolved) {
- return 'unresolved';
- }
- }
-
- return '';
- },
isExpanded() {
return this.discussion.expanded || this.alwaysExpanded;
},
@@ -283,8 +274,9 @@ export default {
this.removePlaceholderNotes();
this.isReplying = true;
this.$nextTick(() => {
- const msg = `Your comment could not be submitted!
-Please check your network connection and try again.`;
+ const msg = __(
+ 'Your comment could not be submitted! Please check your network connection and try again.',
+ );
Flash(msg, 'alert', this.$el);
this.$refs.noteForm.note = noteText;
callback(err);
@@ -312,11 +304,11 @@ Please check your network connection and try again.`;
</script>
<template>
- <timeline-entry-item class="note note-discussion" :class="componentClassName">
+ <timeline-entry-item class="note note-discussion">
<div class="timeline-content">
<div :data-discussion-id="discussion.id" class="discussion js-discussion-container">
<div v-if="shouldRenderDiffs" class="discussion-header note-wrapper">
- <div v-once class="timeline-icon">
+ <div v-once class="timeline-icon align-self-start flex-shrink-0">
<user-avatar-link
v-if="author"
:link-href="author.path"
@@ -325,7 +317,7 @@ Please check your network connection and try again.`;
:img-size="40"
/>
</div>
- <div class="timeline-content">
+ <div class="timeline-content w-100">
<note-header
:author="author"
:created-at="firstNote.created_at"
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index aa80e25a3e0..2f201839d45 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -5,7 +5,7 @@ import { escape } from 'underscore';
import { truncateSha } from '~/lib/utils/text_utility';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import draftMixin from 'ee_else_ce/notes/mixins/draft';
-import { s__, sprintf } from '../../locale';
+import { __, s__, sprintf } from '../../locale';
import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteHeader from './note_header.vue';
@@ -128,9 +128,13 @@ export default {
this.$emit('handleEdit');
},
deleteHandler() {
- const typeOfComment = this.note.isDraft ? 'pending comment' : 'comment';
- // eslint-disable-next-line no-alert
- if (window.confirm(`Are you sure you want to delete this ${typeOfComment}?`)) {
+ const typeOfComment = this.note.isDraft ? __('pending comment') : __('comment');
+ if (
+ // eslint-disable-next-line no-alert
+ window.confirm(
+ sprintf(__('Are you sure you want to delete this %{typeOfComment}?'), { typeOfComment }),
+ )
+ ) {
this.isDeleting = true;
this.$emit('handleDeleteNote', this.note);
@@ -141,7 +145,7 @@ export default {
this.isDeleting = false;
})
.catch(() => {
- Flash('Something went wrong while deleting your note. Please try again.');
+ Flash(__('Something went wrong while deleting your note. Please try again.'));
this.isDeleting = false;
});
}
@@ -185,7 +189,7 @@ export default {
this.isRequesting = false;
this.isEditing = true;
this.$nextTick(() => {
- const msg = 'Something went wrong while editing your comment. Please try again.';
+ const msg = __('Something went wrong while editing your comment. Please try again.');
Flash(msg, 'alert', this.$el);
this.recoverNoteContent(noteText);
callback();
@@ -195,7 +199,7 @@ export default {
formCancelHandler(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
// eslint-disable-next-line no-alert
- if (!window.confirm('Are you sure you want to cancel editing this comment?')) return;
+ if (!window.confirm(__('Are you sure you want to cancel editing this comment?'))) return;
}
this.$refs.noteBody.resetAutoSave();
if (this.oldContent) {
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 4d00e957973..a0695f9e191 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -1,4 +1,5 @@
<script>
+import { __ } from '~/locale';
import { mapGetters, mapActions } from 'vuex';
import { getLocationHash } from '../../lib/utils/url_utility';
import Flash from '../../flash';
@@ -170,7 +171,7 @@ export default {
.catch(() => {
this.setLoadingState(false);
this.setNotesFetchedState(true);
- Flash('Something went wrong while fetching comments. Please try again.');
+ Flash(__('Something went wrong while fetching comments. Please try again.'));
});
},
initPolling() {
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 57dd1c5cab2..c70c0e4095c 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import { isEE } from '~/lib/utils/common_utils';
import initNoteStats from 'ee_else_ce/event_tracking/notes';
import notesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
@@ -41,9 +40,7 @@ document.addEventListener('DOMContentLoaded', () => {
};
},
mounted() {
- if (isEE) {
- initNoteStats();
- }
+ initNoteStats();
},
render(createElement) {
return createElement('notes-app', {
diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js
index 237e70c0a4c..47a6f07cce2 100644
--- a/app/assets/javascripts/notes/services/notes_service.js
+++ b/app/assets/javascripts/notes/services/notes_service.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import Api from '~/api';
import VueResource from 'vue-resource';
import * as constants from '../constants';
@@ -45,7 +44,4 @@ export default {
toggleIssueState(endpoint, data) {
return Vue.http.put(endpoint, data);
},
- applySuggestion(id) {
- return Api.applySuggestion(id);
- },
};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 9054b4779aa..fef962f008e 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -14,6 +14,7 @@ import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils';
import mrWidgetEventHub from '../../vue_merge_request_widget/event_hub';
import { __ } from '~/locale';
+import Api from '~/api';
let eTagPoll;
@@ -449,8 +450,7 @@ export const submitSuggestion = (
{ commit, dispatch },
{ discussionId, noteId, suggestionId, flashContainer },
) =>
- service
- .applySuggestion(suggestionId)
+ Api.applySuggestion(suggestionId)
.then(() => commit(types.APPLY_SUGGESTION, { discussionId, noteId, suggestionId }))
.then(() => dispatch('resolveDiscussion', { discussionId }).catch(() => {}))
.catch(err => {
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 1b56b97f751..d51d411f3c6 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -82,7 +82,7 @@ export default class Todos {
})
.catch(() => {
this.updateRowState(target, true);
- return flash(__('Error updating todo status.'));
+ return flash(__('Error updating status of to-do item.'));
});
}
@@ -124,7 +124,7 @@ export default class Todos {
this.updateAllState(target, data);
this.updateBadges(data);
})
- .catch(() => flash(__('Error updating status for all todos.')));
+ .catch(() => flash(__('Error updating status for all to-do items.')));
}
updateAllState(target, data) {
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index 23fb5656008..dcdee77a8ab 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -1,11 +1,15 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar';
import { FILTERED_SEARCH } from '~/pages/constants';
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import initManualOrdering from '~/manual_ordering';
+const ISSUE_BULK_UPDATE_PREFIX = 'issue_';
+
document.addEventListener('DOMContentLoaded', () => {
IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+ issuableInitBulkUpdateSidebar.init(ISSUE_BULK_UPDATE_PREFIX);
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js
index 941c4552579..2205a7bafe3 100644
--- a/app/assets/javascripts/pages/projects/issues/form.js
+++ b/app/assets/javascripts/pages/projects/issues/form.js
@@ -17,7 +17,5 @@ export default () => {
new MilestoneSelect();
new IssuableTemplateSelectors();
- if (gon.features.graphql) {
- initSuggestions();
- }
+ initSuggestions();
};
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 1e66ccbfa29..0d9e992e596 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -76,7 +76,7 @@ export default {
variables: {
projectPath: this.projectPath,
ref: this.ref,
- path: this.path,
+ path: this.path || '/',
nextPageCursor: this.nextPageCursor,
pageSize: PAGE_SIZE,
},
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index 0ad2b3a73a2..fa6b6bfaef1 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -1,4 +1,6 @@
<script>
+import { n__ } from '~/locale';
+
export default {
name: 'AssigneeTitle',
props: {
@@ -24,7 +26,7 @@ export default {
computed: {
assigneeTitle() {
const assignees = this.numberOfAssignees;
- return assignees > 1 ? `${assignees} Assignees` : 'Assignee';
+ return n__('Assignee', `%d Assignees`, assignees);
},
},
};
@@ -32,18 +34,18 @@ export default {
<template>
<div class="title hide-collapsed">
{{ assigneeTitle }}
- <i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"> </i>
+ <i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i>
<a v-if="editable" class="js-sidebar-dropdown-toggle edit-link float-right" href="#">
{{ __('Edit') }}
</a>
<a
v-if="showToggle"
- aria-label="Toggle sidebar"
+ :aria-label="__('Toggle sidebar')"
class="gutter-toggle float-right js-sidebar-toggle"
href="#"
role="button"
>
- <i aria-hidden="true" data-hidden="true" class="fa fa-angle-double-right"> </i>
+ <i aria-hidden="true" data-hidden="true" class="fa fa-angle-double-right"></i>
</a>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index 0074d7099dc..805c21d0965 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -1,5 +1,5 @@
<script>
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
@@ -62,7 +62,8 @@ export default {
return this.numberOfHiddenAssignees > 0;
},
hiddenAssigneesLabel() {
- return `+ ${this.numberOfHiddenAssignees} more`;
+ const { numberOfHiddenAssignees } = this;
+ return sprintf(__('+ %{numberOfHiddenAssignees} more'), { numberOfHiddenAssignees });
},
collapsedTooltipTitle() {
const maxRender = Math.min(this.defaultRenderCount, this.users.length);
@@ -103,12 +104,15 @@ export default {
// Everyone can merge
return null;
} else if (cannotMergeCount === assigneesCount && assigneesCount > 1) {
- return 'No one can merge';
+ return __('No one can merge');
} else if (assigneesCount === 1) {
- return 'Cannot merge';
+ return __('Cannot merge');
}
- return `${canMergeCount}/${assigneesCount} can merge`;
+ return sprintf(__('%{canMergeCount}/%{assigneesCount} can merge'), {
+ canMergeCount,
+ assigneesCount,
+ });
},
},
methods: {
@@ -128,7 +132,7 @@ export default {
return `${this.rootPath}${user.username}`;
},
assigneeAlt(user) {
- return `${user.name}'s avatar`;
+ return sprintf(__("%{userName}'s avatar"), { userName: user.name });
},
assigneeUsername(user) {
return `@${user.username}`;
@@ -153,7 +157,7 @@ export default {
data-placement="left"
data-boundary="viewport"
>
- <i v-if="hasNoUsers" aria-label="None" class="fa fa-user"> </i>
+ <i v-if="hasNoUsers" :aria-label="__('None')" class="fa fa-user"> </i>
<button
v-for="(user, index) in users"
v-if="shouldRenderCollapsedAssignee(index)"
@@ -185,9 +189,12 @@ export default {
</span>
<template v-if="hasNoUsers">
<span class="assign-yourself no-value qa-assign-yourself">
- None
+ {{ __('None') }}
<template v-if="editable">
- - <button type="button" class="btn-link" @click="assignSelf">assign yourself</button>
+ -
+ <button type="button" class="btn-link" @click="assignSelf">
+ {{ __('assign yourself') }}
+ </button>
</template>
</span>
</template>
@@ -232,9 +239,7 @@ export default {
<template v-if="showLess">
{{ hiddenAssigneesLabel }}
</template>
- <template v-else>
- - show less
- </template>
+ <template v-else>{{ __('- show less') }}</template>
</button>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index cfa7029b388..be1e4811856 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -2,8 +2,10 @@
import Flash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import Store from '~/sidebar/stores/sidebar_store';
+import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import AssigneeTitle from './assignee_title.vue';
import Assignees from './assignees.vue';
+import { __ } from '~/locale';
export default {
name: 'SidebarAssignees',
@@ -72,9 +74,12 @@ export default {
this.mediator
.saveAssignees(this.field)
.then(setLoadingFalse.bind(this))
+ .then(() => {
+ refreshUserMergeRequestCounts();
+ })
.catch(() => {
setLoadingFalse();
- return new Flash('Error occurred when saving assignees');
+ return new Flash(__('Error occurred when saving assignees'));
});
},
},
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
index 4b9bb5c7b0e..5d0e39e8195 100644
--- a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
@@ -1,6 +1,7 @@
<script>
import $ from 'jquery';
import eventHub from '../../event_hub';
+import { __ } from '~/locale';
export default {
props: {
@@ -15,7 +16,7 @@ export default {
},
computed: {
toggleButtonText() {
- return this.isConfidential ? 'Turn Off' : 'Turn On';
+ return this.isConfidential ? __('Turn Off') : __('Turn On');
},
updateConfidentialBool() {
return !this.isConfidential;
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
index 657ac837baf..24d5b14ded9 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
@@ -79,7 +79,7 @@ export default {
} else if (this.showSpentOnlyState) {
return `${this.timeSpent} / --`;
} else if (this.showNoTimeTrackingState) {
- return 'None';
+ return __('None');
}
return '';
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index bc263bc36e4..06aca547183 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -2,6 +2,7 @@
import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import tooltip from '../../../vue_shared/directives/tooltip';
import { GlProgressBar } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
export default {
name: 'TimeTrackingComparisonPane',
@@ -43,8 +44,14 @@ export default {
return stringifyTime(this.parsedTimeRemaining);
},
timeRemainingTooltip() {
- const prefix = this.timeRemainingMinutes < 0 ? 'Over by' : 'Time remaining:';
- return `${prefix} ${this.timeRemainingHumanReadable}`;
+ const { timeRemainingHumanReadable, timeRemainingMinutes } = this;
+ return timeRemainingMinutes < 0
+ ? sprintf(s__('TimeTracking|Over by %{timeRemainingHumanReadable}'), {
+ timeRemainingHumanReadable,
+ })
+ : sprintf(s__('TimeTracking|Time remaining: %{timeRemainingHumanReadable}'), {
+ timeRemainingHumanReadable,
+ });
},
/* Diff values for comparison meter */
timeRemainingMinutes() {
@@ -74,12 +81,12 @@ export default {
<gl-progress-bar :value="timeRemainingPercent" :variant="progressBarVariant" />
<div class="compare-display-container">
<div class="compare-display float-left">
- <span class="compare-label"> {{ s__('TimeTracking|Spent') }} </span>
- <span class="compare-value spent"> {{ timeSpentHumanReadable }} </span>
+ <span class="compare-label">{{ s__('TimeTracking|Spent') }}</span>
+ <span class="compare-value spent">{{ timeSpentHumanReadable }}</span>
</div>
<div class="compare-display estimated float-right">
- <span class="compare-label"> {{ s__('TimeTrackingEstimated|Est') }} </span>
- <span class="compare-value"> {{ timeEstimateHumanReadable }} </span>
+ <span class="compare-label">{{ s__('TimeTrackingEstimated|Est') }}</span>
+ <span class="compare-value">{{ timeEstimateHumanReadable }}</span>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/spent_only_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/spent_only_pane.vue
index 7c7356e2afa..c2f30310e2e 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/spent_only_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/spent_only_pane.vue
@@ -1,4 +1,6 @@
<script>
+import { sprintf, s__ } from '~/locale';
+
export default {
name: 'TimeTrackingSpentOnlyPane',
props: {
@@ -7,11 +9,22 @@ export default {
required: true,
},
},
+ computed: {
+ timeSpent() {
+ return sprintf(
+ s__('TimeTracking|%{startTag}Spent: %{endTag}%{timeSpentHumanReadable}'),
+ {
+ startTag: '<span class="bold">',
+ endTag: '</span>',
+ timeSpentHumanReadable: this.timeSpentHumanReadable,
+ },
+ false,
+ );
+ },
+ },
};
</script>
<template>
- <div class="time-tracking-spend-only-pane">
- <span class="bold">Spent:</span> {{ timeSpentHumanReadable }}
- </div>
+ <div class="time-tracking-spend-only-pane" v-html="timeSpent"></div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
index 57125c78cf6..e6f2fe2b5fc 100644
--- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
+++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue
@@ -5,8 +5,8 @@ import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-const MARK_TEXT = __('Mark todo as done');
-const TODO_TEXT = __('Add todo');
+const MARK_TEXT = __('Mark as done');
+const TODO_TEXT = __('Add a To Do');
export default {
directives: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index 34cdb70ce14..5c7859828d8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -125,7 +125,9 @@ export default {
this.isStopping = false;
})
.catch(() => {
- createFlash('Something went wrong while stopping this environment. Please try again.');
+ createFlash(
+ __('Something went wrong while stopping this environment. Please try again.'),
+ );
this.isStopping = false;
});
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index e20a16900d4..fb826be19f5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -139,7 +139,7 @@ export default {
type="button"
class="btn dropdown-toggle qa-dropdown-toggle"
data-toggle="dropdown"
- aria-label="Download as"
+ :aria-label="__('Download as')"
aria-haspopup="true"
aria-expanded="false"
>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
index 5958c2cf87e..8e8e67228ed 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
@@ -6,6 +6,7 @@ import statusIcon from '../mr_widget_status_icon.vue';
import MrWidgetAuthor from '../../components/mr_widget_author.vue';
import eventHub from '../../event_hub';
import { AUTO_MERGE_STRATEGIES } from '../../constants';
+import { __ } from '~/locale';
export default {
name: 'MRWidgetAutoMergeEnabled',
@@ -55,7 +56,7 @@ export default {
})
.catch(() => {
this.isCancellingAutoMerge = false;
- Flash('Something went wrong. Please try again.');
+ Flash(__('Something went wrong. Please try again.'));
});
},
removeSourceBranch() {
@@ -76,7 +77,7 @@ export default {
})
.catch(() => {
this.isRemovingSourceBranch = false;
- Flash('Something went wrong. Please try again.');
+ Flash(__('Something went wrong. Please try again.'));
});
},
},
@@ -107,15 +108,15 @@ export default {
<section class="mr-info-list">
<p>
{{ s__('mrWidget|The changes will be merged into') }}
- <a :href="mr.targetBranchPath" class="label-branch"> {{ mr.targetBranch }} </a>
+ <a :href="mr.targetBranchPath" class="label-branch">{{ mr.targetBranch }}</a>
</p>
<p v-if="mr.shouldRemoveSourceBranch">
{{ s__('mrWidget|The source branch will be deleted') }}
</p>
<p v-else class="d-flex align-items-start">
- <span class="append-right-10">
- {{ s__('mrWidget|The source branch will not be deleted') }}
- </span>
+ <span class="append-right-10">{{
+ s__('mrWidget|The source branch will not be deleted')
+ }}</span>
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 0bcccc50eb2..c7b064b8506 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -4,6 +4,7 @@ import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon.vue';
import Flash from '../../../flash';
+import { __, sprintf } from '~/locale';
export default {
name: 'MRWidgetRebase',
@@ -40,6 +41,17 @@ export default {
showDisabledButton() {
return ['failed', 'loading'].includes(this.status);
},
+ fastForwardMergeText() {
+ return sprintf(
+ __(
+ `Fast-forward merge is not possible. Rebase the source branch onto %{startTag}${this.mr.targetBranch}%{endTag} to allow this merge request to be merged.`,
+ ),
+ {
+ startTag: '<span class="label-branch">',
+ endTag: '</span>',
+ },
+ );
+ },
},
methods: {
rebase() {
@@ -54,7 +66,7 @@ export default {
.catch(error => {
this.rebasingError = error.merge_error;
this.isMakingRequest = false;
- Flash('Something went wrong. Please try again.');
+ Flash(__('Something went wrong. Please try again.'));
});
},
checkRebaseStatus(continuePolling, stopPolling) {
@@ -69,7 +81,7 @@ export default {
if (res.merge_error && res.merge_error.length) {
this.rebasingError = res.merge_error;
- Flash('Something went wrong. Please try again.');
+ Flash(__('Something went wrong. Please try again.'));
}
eventHub.$emit('MRWidgetRebaseSuccess');
@@ -78,7 +90,7 @@ export default {
})
.catch(() => {
this.isMakingRequest = false;
- Flash('Something went wrong. Please try again.');
+ Flash(__('Something went wrong. Please try again.'));
stopPolling();
});
},
@@ -91,19 +103,14 @@ export default {
<div class="rebase-state-find-class-convention media media-body space-children">
<template v-if="mr.rebaseInProgress || isMakingRequest">
- <span class="bold"> Rebase in progress </span>
+ <span class="bold">{{ __('Rebase in progress') }}</span>
</template>
<template v-if="!mr.rebaseInProgress && !mr.canPushToSourceBranch">
- <span class="bold">
- Fast-forward merge is not possible. Rebase the source branch onto
- <span class="label-branch">{{ mr.targetBranch }}</span> to allow this merge request to be
- merged.
- </span>
+ <span class="bold" v-html="fastForwardMergeText"></span>
</template>
<template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest">
<div
- class="accept-merge-holder clearfix
-js-toggle-container accept-action media space-children"
+ class="accept-merge-holder clearfix js-toggle-container accept-action media space-children"
>
<button
:disabled="isMakingRequest"
@@ -111,14 +118,14 @@ js-toggle-container accept-action media space-children"
class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button"
@click="rebase"
>
- <gl-loading-icon v-if="isMakingRequest" />
- Rebase
+ <gl-loading-icon v-if="isMakingRequest" />{{ __('Rebase') }}
</button>
- <span v-if="!rebasingError" class="bold">
- Fast-forward merge is not possible. Rebase the source branch onto the target branch or
- merge target branch into source branch to allow this merge request to be merged.
- </span>
- <span v-else class="bold danger"> {{ rebasingError }} </span>
+ <span v-if="!rebasingError" class="bold">{{
+ __(
+ 'Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged.',
+ )
+ }}</span>
+ <span v-else class="bold danger">{{ rebasingError }}</span>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
index a38495bb4cc..7312b31c01c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/nothing_to_merge.vue
@@ -22,19 +22,29 @@ export default {
<span v-html="emptyStateSVG"></span>
</div>
<div class="text col-md-7 order-md-first col-12">
- <span>
- Merge requests are a place to propose changes you have made to a project and discuss those
- changes with others.
- </span>
- <p>Interested parties can even contribute by pushing commits if they want to.</p>
+ <span>{{
+ s__(
+ 'mrWidgetNothingToMerge|Merge requests are a place to propose changes you have made to a project and discuss those changes with others.',
+ )
+ }}</span>
<p>
- Currently there are no changes in this merge request's source branch. Please push new
- commits or use a different branch.
+ {{
+ s__(
+ 'mrWidgetNothingToMerge|Interested parties can even contribute by pushing commits if they want to.',
+ )
+ }}
+ </p>
+ <p>
+ {{
+ s__(
+ "mrWidgetNothingToMerge|Currently there are no changes in this merge request's source branch. Please push new commits or use a different branch.",
+ )
+ }}
</p>
<div>
- <a v-if="mr.newBlobPath" :href="mr.newBlobPath" class="btn btn-inverted btn-success">
- Create file
- </a>
+ <a v-if="mr.newBlobPath" :href="mr.newBlobPath" class="btn btn-inverted btn-success">{{
+ __('Create file')
+ }}</a>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index ca1b4a57717..d4514767912 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -6,6 +6,7 @@ import simplePoll from '~/lib/utils/simple_poll';
import { __ } from '~/locale';
import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge';
import MergeRequest from '../../../merge_request';
+import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
@@ -174,6 +175,8 @@ export default {
MergeRequest.decreaseCounter();
stopPolling();
+ refreshUserMergeRequestCounts();
+
// If user checked remove source branch and we didn't remove the branch yet
// we should start another polling for source branch remove process
if (this.removeSourceBranch && data.source_branch_exists) {
@@ -248,7 +251,7 @@ export default {
type="button"
class="btn btn-sm btn-info dropdown-toggle js-merge-moment"
data-toggle="dropdown"
- aria-label="Select merge moment"
+ :aria-label="__('Select merge moment')"
>
<i class="fa fa-chevron-down qa-merge-moment-dropdown" aria-hidden="true"></i>
</button>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index 7c322388d30..91c0b40a0b5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -46,14 +46,20 @@ export default {
<status-icon :show-disabled-button="Boolean(mr.removeWIPPath)" status="warning" />
<div class="media-body space-children">
<span class="bold">
- This is a Work in Progress
+ {{ __('This is a Work in Progress') }}
<i
v-tooltip
class="fa fa-question-circle"
- title="When this merge request is ready,
- remove the WIP: prefix from the title to allow it to be merged"
- aria-label="When this merge request is ready,
- remove the WIP: prefix from the title to allow it to be merged"
+ :title="
+ s__(
+ 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged',
+ )
+ "
+ :aria-label="
+ s__(
+ 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged',
+ )
+ "
>
</i>
</span>
@@ -64,8 +70,8 @@ export default {
class="btn btn-default btn-sm js-remove-wip"
@click="removeWIP"
>
- <i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"> </i> Resolve WIP
- status
+ <i v-if="isMakingRequest" class="fa fa-spinner fa-spin" aria-hidden="true"> </i>
+ {{ s__('mrWidget|Resolve WIP status') }}
</button>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index a79da476890..8d415c1bbea 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -263,8 +263,11 @@ export default {
if (!data.pipeline) return;
const { label } = data.pipeline.details.status;
- const title = `Pipeline ${label}`;
- const message = `Pipeline ${label} for "${data.title}"`;
+ const title = sprintf(__('Pipeline %{label}'), { label });
+ const message = sprintf(__('Pipeline %{label} for "%{dataTitle}"'), {
+ dataTitle: data.title,
+ label,
+ });
notify.notifyMe(title, message, this.mr.gitlabLogo);
},
diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
index e9ab6f5ba7a..15cb0bd9792 100644
--- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue
@@ -1,7 +1,6 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
-import { pluralize } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import { getCommitIconMap } from '~/ide/utils';
@@ -69,7 +68,7 @@ export default {
});
} else if (this.file.changed && this.file.staged) {
return sprintf(__('Unstaged and staged %{type}'), {
- type: pluralize(type),
+ type,
});
}
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index a1168fa0f1e..ae9b013d980 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -1,6 +1,7 @@
<script>
import _ from 'underscore';
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import UserAvatarLink from './user_avatar/user_avatar_link.vue';
import Icon from '../../vue_shared/components/icon.vue';
@@ -129,7 +130,9 @@ export default {
* @returns {String}
*/
userImageAltDescription() {
- return this.author && this.author.username ? `${this.author.username}'s avatar` : null;
+ return this.author && this.author.username
+ ? sprintf(__("%{username}'s avatar"), { username: this.author.username })
+ : null;
},
},
};
@@ -180,7 +183,7 @@ export default {
{{ title }}
</gl-link>
</tooltip-on-truncate>
- <span v-else> Can't find HEAD commit for this branch </span>
+ <span v-else>{{ __("Can't find HEAD commit for this branch") }}</span>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
index ba63683f5c0..da0b45110e2 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/lib/viewer_utils.js
@@ -3,6 +3,7 @@ import { __ } from '~/locale';
const viewers = {
image: {
id: 'image',
+ binary: true,
},
markdown: {
id: 'markdown',
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index 2ca933a37d2..fc6a45b957e 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -91,7 +91,9 @@ export default {
|
</template>
<template v-if="hasDimensions">
- <strong>W</strong>: {{ width }} | <strong>H</strong>: {{ height }}
+ <strong>{{ s__('ImageViewerDimensions|W') }}</strong
+ >: {{ width }} | <strong>{{ s__('ImageViewerDimensions|H') }}</strong
+ >: {{ height }}
</template>
</p>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index 5fdc915fffb..655f0054887 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -40,7 +40,7 @@ export default {
this.fetchMarkdownPreview();
},
destroyed() {
- if (this.isLoading) axiosSource.cancel('Cancelling Preview');
+ if (this.isLoading) axiosSource.cancel(__('Cancelling Preview'));
},
methods: {
fetchMarkdownPreview() {
diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
index 36b3ee05456..d5558d93219 100644
--- a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
@@ -1,5 +1,7 @@
<script>
/* eslint-disable vue/require-default-prop */
+import { __ } from '~/locale';
+
export default {
name: 'DeprecatedModal', // use GlModal instead
@@ -39,7 +41,7 @@ export default {
closeButtonLabel: {
type: String,
required: false,
- default: 'Cancel',
+ default: __('Cancel'),
},
primaryButtonLabel: {
type: String,
@@ -94,7 +96,7 @@ export default {
type="button"
class="close float-right"
data-dismiss="modal"
- aria-label="Close"
+ :aria-label="__('Close')"
@click="emitCancel($event)"
>
<span aria-hidden="true">&times;</span>
diff --git a/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue b/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue
index 7d49c87271d..c35fee84771 100644
--- a/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/droplab_dropdown_button.vue
@@ -69,7 +69,7 @@ export default {
data-display="static"
data-toggle="dropdown"
>
- <icon name="arrow-down" aria-label="toggle dropdown" />
+ <icon name="arrow-down" :aria-label="__('toggle dropdown')" />
</button>
<ul :class="dropdownClass" class="dropdown-menu dropdown-open-top">
<template v-for="(action, index) in actions">
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
index 4e5dfbf3bf8..20bcceeb477 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_dropdown.vue
@@ -115,7 +115,7 @@ export default {
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
- aria-label="Expand dropdown"
+ :aria-label="__('Expand dropdown')"
>
<icon name="angle-down" :size="12" />
</button>
@@ -125,7 +125,7 @@ export default {
ref="searchInput"
v-model="filter"
type="search"
- placeholder="Filter"
+ :placeholder="__('Filter')"
class="js-filtered-dropdown-input dropdown-input-field"
/>
<icon class="dropdown-input-search" name="search" />
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 3f45dc7853b..c652a684d7c 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -1,5 +1,6 @@
<script>
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
import CiIconBadge from './ci_badge_link.vue';
import TimeagoTooltip from './time_ago_tooltip.vue';
import UserAvatarImage from './user_avatar/user_avatar_image.vue';
@@ -65,7 +66,7 @@ export default {
computed: {
userAvatarAltText() {
- return `${this.user.name}'s avatar`;
+ return sprintf(__(`%{username}'s avatar`), { username: this.user.name });
},
},
@@ -87,16 +88,12 @@ export default {
<strong> {{ itemName }} #{{ itemId }} </strong>
- <template v-if="shouldRenderTriggeredLabel">
- triggered
- </template>
- <template v-else>
- created
- </template>
+ <template v-if="shouldRenderTriggeredLabel">{{ __('triggered') }}</template>
+ <template v-else>{{ __('created') }}</template>
<timeago-tooltip :time="time" />
- by
+ {{ __('by') }}
<template v-if="user">
<gl-link
diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
index e438ff16a41..47f0851f650 100644
--- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
@@ -1,7 +1,7 @@
<script>
import { GlLink } from '@gitlab/ui';
import _ from 'underscore';
-import { sprintf } from '~/locale';
+import { __, sprintf } from '~/locale';
import icon from '../../../vue_shared/components/icon.vue';
function buildDocsLinkStart(path) {
@@ -47,7 +47,9 @@ export default {
},
confidentialAndLockedDiscussionText() {
return sprintf(
- 'This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}.',
+ __(
+ 'This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}.',
+ ),
{
confidentialLinkStart: buildDocsLinkStart(this.confidentialIssueDocsPath),
lockedLinkStart: buildDocsLinkStart(this.lockedIssueDocsPath),
@@ -66,7 +68,7 @@ export default {
<span v-if="isLockedAndConfidential">
<span v-html="confidentialAndLockedDiscussionText"></span>
{{
- __(`People without permission will never get a notification and won't be able to comment.`)
+ __("People without permission will never get a notification and won't be able to comment.")
}}
</span>
diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
index eb0f666422f..b76679960ca 100644
--- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
@@ -160,8 +160,8 @@ export default {
:disabled="removeDisabled"
type="button"
class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center"
- title="Remove"
- aria-label="Remove"
+ :title="__('Remove')"
+ :aria-label="__('Remove')"
@click="onRemoveRequest"
>
<icon :size="16" class="btn-item-remove-icon" name="close" />
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 3bdc0bb8ebd..b520d302407 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,7 +1,7 @@
<script>
import $ from 'jquery';
import _ from 'underscore';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import { stripHtml } from '~/lib/utils/text_utility';
import Flash from '../../../flash';
import GLForm from '../../../gl_form';
@@ -118,6 +118,18 @@ export default {
lineType() {
return this.line ? this.line.type : '';
},
+ addMultipleToDiscussionWarning() {
+ return sprintf(
+ __(
+ '%{icon}You are about to add %{usersTag} people to the discussion. Proceed with caution.',
+ ),
+ {
+ icon: '<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>',
+ usersTag: `<strong><span class="js-referenced-users-count">${this.referencedUsers.length}</span></strong>`,
+ },
+ false,
+ );
+ },
},
mounted() {
/*
@@ -172,7 +184,7 @@ export default {
renderMarkdown(data = {}) {
this.markdownPreviewLoading = false;
- this.markdownPreview = data.body || 'Nothing to preview.';
+ this.markdownPreview = data.body || __('Nothing to preview.');
if (data.references) {
this.referencedCommands = data.references.commands;
@@ -207,7 +219,11 @@ export default {
<div v-show="!previewMarkdown" class="md-write-holder">
<div class="zen-backdrop">
<slot name="textarea"></slot>
- <a class="zen-control zen-control-leave js-zen-leave" href="#" aria-label="Enter zen mode">
+ <a
+ class="zen-control zen-control-leave js-zen-leave"
+ href="#"
+ :aria-label="__('Enter zen mode')"
+ >
<icon :size="32" name="screen-normal" />
</a>
<markdown-toolbar
@@ -246,13 +262,7 @@ export default {
<template v-if="previewMarkdown && !markdownPreviewLoading">
<div v-if="referencedCommands" class="referenced-commands" v-html="referencedCommands"></div>
<div v-if="shouldShowReferencedUsers" class="referenced-users">
- <span>
- <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> You are about to add
- <strong>
- <span class="js-referenced-users-count">{{ referencedUsers.length }}</span>
- </strong>
- people to the discussion. Proceed with caution.
- </span>
+ <span v-html="addMultipleToDiscussionWarning"></span>
</div>
</template>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
index 8d3705e1e4a..7f0fcfac071 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
@@ -1,5 +1,6 @@
<script>
import Vue from 'vue';
+import { __ } from '~/locale';
import SuggestionDiff from './suggestion_diff.vue';
import Flash from '~/flash';
@@ -56,7 +57,7 @@ export default {
const suggestionElements = container.querySelectorAll('.js-render-suggestion');
if (this.lineType === 'old') {
- Flash('Unable to apply suggestions to a deleted line.', 'alert', this.$el);
+ Flash(__('Unable to apply suggestions to a deleted line.'), 'alert', this.$el);
}
suggestionElements.forEach((suggestionEl, i) => {
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index d6c398c8946..8ce5b615795 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -33,13 +33,18 @@ export default {
<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
- >
+ <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>
+ <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>
</div>
@@ -57,15 +62,17 @@ export default {
<i class="fa fa-file-image-o toolbar-button-icon" aria-hidden="true"></i>
</span>
<span class="uploading-error-message"></span>
- <button class="retry-uploading-link" type="button">Try again</button> or
- <button class="attach-new-file markdown-selector" type="button">attach a new file</button>
+ <button class="retry-uploading-link" type="button">{{ __('Try again') }}</button> or
+ <button class="attach-new-file markdown-selector" type="button">
+ {{ __('attach a new file') }}
+ </button>
</span>
<button class="markdown-selector button-attach-file btn-link" tabindex="-1" type="button">
<i class="fa fa-file-image-o toolbar-button-icon" aria-hidden="true"></i
- ><span class="text-attach-file">Attach a file</span>
+ ><span class="text-attach-file">{{ __('Attach a file') }}</span>
</button>
<button class="btn btn-default btn-sm hide button-cancel-uploading-files" type="button">
- Cancel
+ {{ __('Cancel') }}
</button>
</span>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index 16f4ff068f6..26d7d8e8866 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -1,4 +1,5 @@
<script>
+import { __, sprintf } from '~/locale';
import { getTimeago } from '../../lib/utils/datetime_utility';
export default {
@@ -20,7 +21,7 @@ export default {
computed: {
getFormattedMedian() {
const deployedSince = getTimeago().format(this.deploymentTime * 1000);
- return `Deployed ${deployedSince}`;
+ return sprintf(__('Deployed %{deployedSince}'), { deployedSince });
},
},
mounted() {
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 3c86b7e4c61..d6dfe9eded8 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -103,7 +103,7 @@ export default {
<div v-if="hasMoreCommits" class="flex-list">
<div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded">
<icon :name="toggleIcon" :size="8" class="append-right-5" />
- <span>Toggle commit list</span>
+ <span>{{ __('Toggle commit list') }}</span>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
index b9311d65360..43bbb756805 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
@@ -14,7 +14,7 @@
/>
*/
-
+import { __ } from '~/locale';
import defaultAvatarUrl from 'images/no_avatar.png';
import { placeholderImage } from '../../../lazy_loader';
@@ -39,7 +39,7 @@ export default {
imgAlt: {
type: String,
required: false,
- default: 'project avatar',
+ default: __('project avatar'),
},
size: {
type: Number,
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
index b5e43da401e..4dcc121496c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue
@@ -85,7 +85,7 @@ export default {
@click="toggleSidebar"
>
<span class="sidebar-collapsed-value">
- <span v-if="showFromText">From</span> <span>{{ dateText('min') }}</span>
+ <span v-if="showFromText">{{ __('From') }}</span> <span>{{ dateText('min') }}</span>
</span>
</collapsed-calendar-icon>
<div v-if="hasMinAndMaxDates" class="text-center sidebar-collapsed-divider">-</div>
@@ -96,7 +96,7 @@ export default {
@click="toggleSidebar"
>
<span class="sidebar-collapsed-value">
- <span v-if="!minDate">Until</span> <span>{{ dateText('max') }}</span>
+ <span v-if="!minDate">{{ __('Until') }}</span> <span>{{ dateText('max') }}</span>
</span>
</collapsed-calendar-icon>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
index 45f01a6fced..6caf8bc92c2 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
@@ -74,7 +74,7 @@ export default {
return dateInWords(this.selectedDate, true);
},
collapsedText() {
- return this.selectedDateWords ? this.selectedDateWords : 'None';
+ return this.selectedDateWords ? this.selectedDateWords : __('None');
},
},
methods: {
@@ -112,7 +112,7 @@ export default {
class="btn-blank btn-link btn-primary-hover-link btn-sidebar-action"
@click="toggleDatePicker"
>
- Edit
+ {{ __('Edit') }}
</button>
<toggle-sidebar v-if="showToggleSidebar" :collapsed="collapsed" @toggle="toggleSidebar" />
</div>
@@ -137,11 +137,11 @@ export default {
class="btn-blank btn-link btn-secondary-hover-link"
@click="newDateSelected(null)"
>
- remove
+ {{ __('remove') }}
</button>
</span>
</template>
- <span v-else class="no-value"> None </span>
+ <span v-else class="no-value">{{ __('None') }}</span>
</span>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
index 3b5ce0e9910..913c971a512 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
@@ -48,7 +48,7 @@ export default {
'fa-angle-double-right': !collapsed,
'fa-angle-double-left': collapsed,
}"
- aria-label="toggle collapse"
+ :aria-label="__('toggle collapse')"
class="fa"
>
</i>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
index a6c1737dcab..ea483416c46 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
@@ -17,6 +17,7 @@
import { GlTooltip } from '@gitlab/ui';
import defaultAvatarUrl from 'images/no_avatar.png';
+import { __ } from '~/locale';
import { placeholderImage } from '../../../lazy_loader';
export default {
@@ -43,7 +44,7 @@ export default {
imgAlt: {
type: String,
required: false,
- default: 'user avatar',
+ default: __('user avatar'),
},
size: {
type: Number,
diff --git a/app/assets/javascripts/vue_shared/mixins/is_ee.js b/app/assets/javascripts/vue_shared/mixins/is_ee.js
deleted file mode 100644
index 8e00d93ef18..00000000000
--- a/app/assets/javascripts/vue_shared/mixins/is_ee.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import Vue from 'vue';
-import { isEE } from '~/lib/utils/common_utils';
-
-Vue.mixin({
- computed: {
- isEE() {
- return isEE();
- },
- },
-});
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index cd951f67293..e75c1379dfb 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -288,7 +288,7 @@
padding: 0 1px;
a,
- button,
+ button:not(.dropdown-toggle,.ci-action-icon-container),
.menu-item {
@include dropdown-link;
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index f75e5b55506..975dca168d5 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -35,7 +35,8 @@
background-color: $modal-body-bg;
line-height: $line-height-base;
position: relative;
- padding: #{3 * $grid-size} #{2 * $grid-size};
+ min-height: $modal-body-height;
+ padding: #{2 * $grid-size} #{6 * $grid-size} #{2 * $grid-size} #{2 * $grid-size};
text-align: left;
white-space: normal;
@@ -85,9 +86,9 @@ body.modal-open {
.modal {
background-color: $black-transparent;
- @include media-breakpoint-up(md) {
+ @include media-breakpoint-up(sm) {
.modal-dialog {
- margin: 30px auto;
+ margin: 64px auto;
}
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index b6a24247d40..406bcda418e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -805,7 +805,7 @@ $border-color-settings: #e1e1e1;
/*
Modals
*/
-$modal-body-height: 134px;
+$modal-body-height: 80px;
$modal-border-color: #e9ecef;
$priority-label-empty-state-width: 114px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 623c44e062f..3ffe8ae304d 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1095,6 +1095,10 @@ table.code {
.discussion-collapsible {
margin: 0 $gl-padding $gl-padding 71px;
+
+ .notes {
+ border-radius: $border-radius-default;
+ }
}
.parallel {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 7bd1a4138e4..b9b8eabf909 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -139,7 +139,6 @@ $note-form-margin-left: 72px;
border-radius: 4px 4px 0 0;
&.collapsed {
- border: 0;
border-radius: 4px;
}
}
@@ -406,7 +405,7 @@ $note-form-margin-left: 72px;
border-radius: 0;
@media (min-width: map-get($grid-breakpoints, md)) {
- top: 91px;
+ top: $mr-tabs-height + $header-height;
.with-performance-bar & {
top: 126px;
@@ -598,7 +597,8 @@ $note-form-margin-left: 72px;
}
.discussion-header {
- min-height: 74px;
+ min-height: $line-height-base * 2em;
+ box-sizing: content-box;
.note-header-info {
padding-bottom: 0;
@@ -608,13 +608,10 @@ $note-form-margin-left: 72px;
overflow-x: auto;
overflow-y: hidden;
}
-}
-.unresolved {
- .discussion-header {
- .note-header-info {
- margin-top: $gl-padding-8;
- }
+ &.note-wrapper {
+ display: flex;
+ align-items: center;
}
}
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index 10120a472d3..60400f10ca5 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -168,6 +168,10 @@
}
ul.wiki-pages-list.content-list {
+ a {
+ color: $blue-600;
+ }
+
ul {
list-style: none;
margin-left: 0;
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 065d2d3a4ec..6fa2f75be33 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -92,7 +92,7 @@ module IssuableActions
end
def bulk_update
- result = Issuable::BulkUpdateService.new(project, current_user, bulk_update_params).execute(resource_name)
+ result = Issuable::BulkUpdateService.new(current_user, bulk_update_params).execute(resource_name)
quantity = result[:count]
render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" }
@@ -181,7 +181,7 @@ module IssuableActions
end
def authorize_admin_issuable!
- unless can?(current_user, :"admin_#{resource_name}", @project) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ unless can?(current_user, :"admin_#{resource_name}", parent)
return access_denied!
end
end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index d43f5393ecc..daeb8fda417 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -7,8 +7,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
before_action :set_non_archived_param
- before_action :projects, only: [:index]
before_action :default_sorting
+ before_action :projects, only: [:index]
skip_cross_project_access_check :index, :starred
def index
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 27980466a42..8f6fcb362d2 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -22,7 +22,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
format.html do
redirect_to dashboard_todos_path,
status: 302,
- notice: _('Todo was successfully marked as done.')
+ notice: _('To-do item successfully marked as done.')
end
format.js { head :ok }
format.json { render json: todos_counts }
@@ -33,7 +33,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
updated_ids = TodoService.new.mark_todos_as_done(@todos, current_user)
respond_to do |format|
- format.html { redirect_to dashboard_todos_path, status: 302, notice: _('All todos were marked as done.') }
+ format.html { redirect_to dashboard_todos_path, status: 302, notice: _('Everything on your to-do list is marked as done.') }
format.js { head :ok }
format.json { render json: todos_counts.merge(updated_ids: updated_ids) }
end
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 1ce0afac83b..9fbbe373b0d 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -11,7 +11,6 @@ class GraphqlController < ApplicationController
# around in GraphiQL.
protect_from_forgery with: :null_session, only: :execute
- before_action :check_graphql_feature_flag!
before_action :authorize_access_api!
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
@@ -86,8 +85,4 @@ class GraphqlController < ApplicationController
render json: error, status: status
end
-
- def check_graphql_feature_flag!
- render_404 unless Gitlab::Graphql.enabled?
- end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d77f64a84f5..141a7dfb923 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -169,7 +169,7 @@ class Projects::BranchesController < Projects::ApplicationController
end
def confidential_issue_project
- return unless Feature.enabled?(:create_confidential_merge_request, @project)
+ return unless helpers.create_confidential_merge_request_enabled?
return if params[:confidential_issue_project_id].blank?
confidential_issue_project = Project.find(params[:confidential_issue_project_id])
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index 0a009477d61..32111b07a0b 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -15,24 +15,22 @@ class Projects::DeploymentsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def metrics
- return render_404 unless deployment.has_metrics?
+ return render_404 unless deployment_metrics.has_metrics?
- @metrics = deployment.metrics
+ @metrics = deployment_metrics.metrics
if @metrics&.any?
render json: @metrics, status: :ok
else
head :no_content
end
- rescue NotImplementedError
- render_404
end
def additional_metrics
- return render_404 unless deployment.has_metrics?
+ return render_404 unless deployment_metrics.has_metrics?
respond_to do |format|
format.json do
- metrics = deployment.additional_metrics
+ metrics = deployment_metrics.additional_metrics
if metrics.any?
render json: metrics
@@ -45,6 +43,10 @@ class Projects::DeploymentsController < Projects::ApplicationController
private
+ def deployment_metrics
+ @deployment_metrics ||= DeploymentMetrics.new(deployment.project, deployment)
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def deployment
@deployment ||= environment.deployments.find_by(iid: params[:id])
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index e275b417784..228de8bc6f3 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -45,8 +45,6 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :authorize_import_issues!, only: [:import_csv]
before_action :authorize_download_code!, only: [:related_branches]
- before_action :set_suggested_issues_feature_flags, only: [:new]
-
respond_to :html
def index
@@ -172,7 +170,7 @@ class Projects::IssuesController < Projects::ApplicationController
def create_merge_request
create_params = params.slice(:branch_name, :ref).merge(issue_iid: issue.iid)
- create_params[:target_project_id] = params[:target_project_id] if Feature.enabled?(:create_confidential_merge_request, @project)
+ create_params[:target_project_id] = params[:target_project_id] if helpers.create_confidential_merge_request_enabled?
result = ::MergeRequests::CreateFromIssueService.new(project, current_user, create_params).execute
if result[:status] == :success
@@ -285,8 +283,4 @@ class Projects::IssuesController < Projects::ApplicationController
# 3. https://gitlab.com/gitlab-org/gitlab-ce/issues/42426
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42422')
end
-
- def set_suggested_issues_feature_flags
- push_frontend_feature_flag(:graphql, default_enabled: true)
- end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 7ee8e0ea8f8..2aa2508be16 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -201,7 +201,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def rebase
- RebaseWorker.perform_async(@merge_request.id, current_user.id)
+ @merge_request.rebase_async(current_user.id)
head :ok
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 330e2d0f8a5..feefc7f8137 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -182,7 +182,7 @@ class ProjectsController < Projects::ApplicationController
end
def housekeeping
- ::Projects::HousekeepingService.new(@project).execute
+ ::Projects::HousekeepingService.new(@project, :gc).execute
redirect_to(
project_path(@project),
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 3592505a977..f4fbeacfaba 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -429,7 +429,7 @@ class IssuableFinder
items = klass.with(cte.to_arel).from(klass.table_name)
end
- items.full_search(search, matched_columns: params[:in])
+ items.full_search(search, matched_columns: params[:in], use_minimum_char_limit: !use_cte_for_search?)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/runner_jobs_finder.rb b/app/finders/runner_jobs_finder.rb
index 4fca4ec94f3..ef90817416a 100644
--- a/app/finders/runner_jobs_finder.rb
+++ b/app/finders/runner_jobs_finder.rb
@@ -3,6 +3,8 @@
class RunnerJobsFinder
attr_reader :runner, :params
+ ALLOWED_INDEXED_COLUMNS = %w[id].freeze
+
def initialize(runner, params = {})
@runner = runner
@params = params
@@ -11,7 +13,7 @@ class RunnerJobsFinder
def execute
items = @runner.builds
items = by_status(items)
- items
+ sort_items(items)
end
private
@@ -23,4 +25,19 @@ class RunnerJobsFinder
items.where(status: params[:status])
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def sort_items(items)
+ return items unless ALLOWED_INDEXED_COLUMNS.include?(params[:order_by])
+
+ order_by = params[:order_by]
+ sort = if /\A(ASC|DESC)\z/i.match?(params[:sort])
+ params[:sort]
+ else
+ :desc
+ end
+
+ items.order(order_by => sort)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index 5615909c4ec..152ebb930e2 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -13,6 +13,7 @@ class GitlabSchema < GraphQL::Schema
use BatchLoader::GraphQL
use Gitlab::Graphql::Authorize
use Gitlab::Graphql::Present
+ use Gitlab::Graphql::CallsGitaly
use Gitlab::Graphql::Connections
use Gitlab::Graphql::GenericTracing
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index dd0d9105df6..efeee4a7a4d 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -7,18 +7,34 @@ module Types
DEFAULT_COMPLEXITY = 1
def initialize(*args, **kwargs, &block)
+ @calls_gitaly = !!kwargs.delete(:calls_gitaly)
+ @constant_complexity = !!kwargs[:complexity]
kwargs[:complexity] ||= field_complexity(kwargs[:resolver_class])
super(*args, **kwargs, &block)
end
+ def base_complexity
+ complexity = DEFAULT_COMPLEXITY
+ complexity += 1 if calls_gitaly?
+ complexity
+ end
+
+ def calls_gitaly?
+ @calls_gitaly
+ end
+
+ def constant_complexity?
+ @constant_complexity
+ end
+
private
def field_complexity(resolver_class)
if resolver_class
field_resolver_complexity
else
- DEFAULT_COMPLEXITY
+ base_complexity
end
end
@@ -31,6 +47,7 @@ module Types
proc do |ctx, args, child_complexity|
# Resolvers may add extra complexity depending on used arguments
complexity = child_complexity + self.resolver&.try(:resolver_complexity, args, child_complexity: child_complexity).to_i
+ complexity += 1 if calls_gitaly?
field_defn = to_graphql
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 577ccd48ef8..6734d4761c2 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -43,7 +43,7 @@ module Types
field :allow_collaboration, GraphQL::BOOLEAN_TYPE, null: true
field :should_be_rebased, GraphQL::BOOLEAN_TYPE, method: :should_be_rebased?, null: false
field :rebase_commit_sha, GraphQL::STRING_TYPE, null: true
- field :rebase_in_progress, GraphQL::BOOLEAN_TYPE, method: :rebase_in_progress?, null: false
+ field :rebase_in_progress, GraphQL::BOOLEAN_TYPE, method: :rebase_in_progress?, null: false, calls_gitaly: true
field :merge_commit_message, GraphQL::STRING_TYPE, method: :default_merge_commit_message, null: true, deprecation_reason: "Renamed to defaultMergeCommitMessage"
field :default_merge_commit_message, GraphQL::STRING_TYPE, null: true
field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 6ef1d816b7c..bc5fb709522 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -9,6 +9,6 @@ module Types
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
- mount_mutation Mutations::MergeRequests::SetWip
+ mount_mutation Mutations::MergeRequests::SetWip, calls_gitaly: true
end
end
diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb
index 13995d3ea8f..d877fc177d2 100644
--- a/app/graphql/types/permission_types/merge_request.rb
+++ b/app/graphql/types/permission_types/merge_request.rb
@@ -10,8 +10,8 @@ module Types
abilities :read_merge_request, :admin_merge_request,
:update_merge_request, :create_note
- permission_field :push_to_source_branch, method: :can_push_to_source_branch?
- permission_field :remove_source_branch, method: :can_remove_source_branch?
+ permission_field :push_to_source_branch, method: :can_push_to_source_branch?, calls_gitaly: true
+ permission_field :remove_source_branch, method: :can_remove_source_branch?, calls_gitaly: true
permission_field :cherry_pick_on_current_merge_request, method: :can_cherry_pick_on_current_merge_request?
permission_field :revert_on_current_merge_request, method: :can_revert_on_current_merge_request?
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index c25688ab043..13be71c26ee 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -26,7 +26,7 @@ module Types
field :web_url, GraphQL::STRING_TYPE, null: true
field :star_count, GraphQL::INT_TYPE, null: false
- field :forks_count, GraphQL::INT_TYPE, null: false
+ field :forks_count, GraphQL::INT_TYPE, null: false, calls_gitaly: true # 4 times
field :created_at, Types::TimeType, null: true
field :last_activity_at, Types::TimeType, null: true
@@ -40,7 +40,7 @@ module Types
field :lfs_enabled, GraphQL::BOOLEAN_TYPE, null: true
field :merge_requests_ff_only_enabled, GraphQL::BOOLEAN_TYPE, null: true
- field :avatar_url, GraphQL::STRING_TYPE, null: true, resolve: -> (project, args, ctx) do
+ field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true, resolve: -> (project, args, ctx) do
project.avatar_url(only_path: false)
end
diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb
index 5987467e1ea..b024eca61fc 100644
--- a/app/graphql/types/repository_type.rb
+++ b/app/graphql/types/repository_type.rb
@@ -6,9 +6,9 @@ module Types
authorize :download_code
- field :root_ref, GraphQL::STRING_TYPE, null: true
- field :empty, GraphQL::BOOLEAN_TYPE, null: false, method: :empty?
+ field :root_ref, GraphQL::STRING_TYPE, null: true, calls_gitaly: true
+ field :empty, GraphQL::BOOLEAN_TYPE, null: false, method: :empty?, calls_gitaly: true
field :exists, GraphQL::BOOLEAN_TYPE, null: false, method: :exists?
- field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver
+ field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true
end
end
diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb
index b947713074e..fbdc1597461 100644
--- a/app/graphql/types/tree/tree_type.rb
+++ b/app/graphql/types/tree/tree_type.rb
@@ -7,7 +7,7 @@ module Types
graphql_name 'Tree'
# Complexity 10 as it triggers a Gitaly call on each render
- field :last_commit, Types::CommitType, null: true, complexity: 10, resolve: -> (tree, args, ctx) do
+ field :last_commit, Types::CommitType, null: true, complexity: 10, calls_gitaly: true, resolve: -> (tree, args, ctx) do
tree.repository.last_commit_for_path(tree.sha, tree.path)
end
@@ -15,9 +15,9 @@ module Types
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
end
- field :submodules, Types::Tree::SubmoduleType.connection_type, null: false
+ field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true
- field :blobs, Types::Tree::BlobType.connection_type, null: false, resolve: -> (obj, args, ctx) do
+ field :blobs, Types::Tree::BlobType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index a7a4e945a99..4bf9b708401 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -187,6 +187,8 @@ module ApplicationSettingsHelper
:gitaly_timeout_default,
:gitaly_timeout_medium,
:gitaly_timeout_fast,
+ :grafana_enabled,
+ :grafana_url,
:gravatar_enabled,
:hashed_storage_enabled,
:help_page_hide_commercial_content,
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 076976175a9..31c4b27273b 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module AuthHelper
- PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
+ PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce).freeze
LDAP_PROVIDER = /\Aldap/.freeze
def ldap_enabled?
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index cd2669ef6ad..67685ba4e1d 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -390,8 +390,8 @@ module IssuablesHelper
def issuable_todo_button_data(issuable, is_collapsed)
{
- todo_text: _('Add todo'),
- mark_text: _('Mark todo as done'),
+ todo_text: _('Add a To Do'),
+ mark_text: _('Mark as done'),
todo_icon: sprite_icon('todo-add'),
mark_icon: sprite_icon('todo-done', css_class: 'todo-undone'),
issuable_id: issuable[:id],
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index dfadcfc33b2..5476a7cdff6 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -137,7 +137,7 @@ module IssuesHelper
end
def create_confidential_merge_request_enabled?
- Feature.enabled?(:create_confidential_merge_request, @project)
+ Feature.enabled?(:create_confidential_merge_request, @project, default_enabled: true)
end
def show_new_branch_button?
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 766508b6609..3672d8b1b03 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -16,7 +16,7 @@ module PreferencesHelper
project_activity: _("Your Projects' Activity"),
starred_project_activity: _("Starred Projects' Activity"),
groups: _("Your Groups"),
- todos: _("Your Todos"),
+ todos: _("Your To-Do List"),
issues: _("Assigned Issues"),
merge_requests: _("Assigned Merge Requests"),
operations: _("Operations Dashboard")
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 8dee842a22d..8d0079a4dd3 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -662,6 +662,6 @@ module ProjectsHelper
end
def vue_file_list_enabled?
- Gitlab::Graphql.enabled? && Feature.enabled?(:vue_file_list, @project)
+ Feature.enabled?(:vue_file_list, @project)
end
end
diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb
index ecf37bae6b3..ce810433a3a 100644
--- a/app/helpers/storage_helper.rb
+++ b/app/helpers/storage_helper.rb
@@ -17,6 +17,6 @@ module StorageHelper
counter_lfs_objects: storage_counter(statistics.lfs_objects_size)
}
- _("%{counter_repositories} repositories, %{counter_wikis} wikis, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS") % counters
+ _("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}") % counters
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index fd5aa216174..20ca4a9ab24 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -790,6 +790,10 @@ module Ci
stages.find_by!(name: name)
end
+ def error_messages
+ errors ? errors.full_messages.to_sentence : ""
+ end
+
private
def ci_yaml_from_repo
diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb
new file mode 100644
index 00000000000..dab034b7234
--- /dev/null
+++ b/app/models/clusters/clusters_hierarchy.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+module Clusters
+ class ClustersHierarchy
+ DEPTH_COLUMN = :depth
+
+ def initialize(clusterable)
+ @clusterable = clusterable
+ end
+
+ # Returns clusters in order from deepest to highest group
+ def base_and_ancestors
+ cte = recursive_cte
+ cte_alias = cte.table.alias(model.table_name)
+
+ model
+ .unscoped
+ .where('clusters.id IS NOT NULL')
+ .with
+ .recursive(cte.to_arel)
+ .from(cte_alias)
+ .order(DEPTH_COLUMN => :asc)
+ end
+
+ private
+
+ attr_reader :clusterable
+
+ def recursive_cte
+ cte = Gitlab::SQL::RecursiveCTE.new(:clusters_cte)
+
+ base_query = case clusterable
+ when ::Group
+ group_clusters_base_query
+ when ::Project
+ project_clusters_base_query
+ else
+ raise ArgumentError, "unknown type for #{clusterable}"
+ end
+
+ cte << base_query
+ cte << parent_query(cte)
+
+ cte
+ end
+
+ def group_clusters_base_query
+ group_parent_id_alias = alias_as_column(groups[:parent_id], 'group_parent_id')
+ join_sources = ::Group.left_joins(:clusters).join_sources
+
+ model
+ .unscoped
+ .select([clusters_star, group_parent_id_alias, "1 AS #{DEPTH_COLUMN}"])
+ .where(groups[:id].eq(clusterable.id))
+ .from(groups)
+ .joins(join_sources)
+ end
+
+ def project_clusters_base_query
+ projects = ::Project.arel_table
+ project_parent_id_alias = alias_as_column(projects[:namespace_id], 'group_parent_id')
+ join_sources = ::Project.left_joins(:clusters).join_sources
+
+ model
+ .unscoped
+ .select([clusters_star, project_parent_id_alias, "1 AS #{DEPTH_COLUMN}"])
+ .where(projects[:id].eq(clusterable.id))
+ .from(projects)
+ .joins(join_sources)
+ end
+
+ def parent_query(cte)
+ group_parent_id_alias = alias_as_column(groups[:parent_id], 'group_parent_id')
+
+ model
+ .unscoped
+ .select([clusters_star, group_parent_id_alias, cte.table[DEPTH_COLUMN] + 1])
+ .from([cte.table, groups])
+ .joins('LEFT OUTER JOIN cluster_groups ON cluster_groups.group_id = namespaces.id')
+ .joins('LEFT OUTER JOIN clusters ON cluster_groups.cluster_id = clusters.id')
+ .where(groups[:id].eq(cte.table[:group_parent_id]))
+ end
+
+ def model
+ Clusters::Cluster
+ end
+
+ def clusters
+ @clusters ||= model.arel_table
+ end
+
+ def groups
+ @groups ||= ::Group.arel_table
+ end
+
+ def clusters_star
+ @clusters_star ||= clusters[Arel.star]
+ 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/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 5a358ae2ef6..8f28c897eb6 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -12,11 +12,26 @@ module DeploymentPlatform
private
def find_deployment_platform(environment)
- find_cluster_platform_kubernetes(environment: environment) ||
- find_group_cluster_platform_kubernetes(environment: environment) ||
+ find_platform_kubernetes(environment) ||
find_instance_cluster_platform_kubernetes(environment: environment)
end
+ def find_platform_kubernetes(environment)
+ if Feature.enabled?(:clusters_cte)
+ find_platform_kubernetes_with_cte(environment)
+ else
+ find_cluster_platform_kubernetes(environment: environment) ||
+ find_group_cluster_platform_kubernetes(environment: environment)
+ end
+ end
+
+ # EE would override this and utilize environment argument
+ def find_platform_kubernetes_with_cte(_environment)
+ Clusters::ClustersHierarchy.new(self).base_and_ancestors
+ .enabled.default_environment
+ .first&.platform_kubernetes
+ end
+
# EE would override this and utilize environment argument
def find_cluster_platform_kubernetes(environment: nil)
clusters.enabled.default_environment
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 299e413321d..952de92cae1 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -168,7 +168,7 @@ module Issuable
# matched_columns - Modify the scope of the query. 'title', 'description' or joining them with a comma.
#
# Returns an ActiveRecord::Relation.
- def full_search(query, matched_columns: 'title,description')
+ def full_search(query, matched_columns: 'title,description', use_minimum_char_limit: true)
allowed_columns = [:title, :description]
matched_columns = matched_columns.to_s.split(',').map(&:to_sym)
matched_columns &= allowed_columns
@@ -176,7 +176,7 @@ module Issuable
# Matching title or description if the matched_columns did not contain any allowed columns.
matched_columns = [:title, :description] if matched_columns.empty?
- fuzzy_search(query, matched_columns)
+ fuzzy_search(query, matched_columns, use_minimum_char_limit: use_minimum_char_limit)
end
def simple_sorts
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index 6c3962b4c4f..d91be73d6f0 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -173,12 +173,16 @@ module ReactiveCaching
end
def within_reactive_cache_lifetime?(*args)
- !!Rails.cache.read(alive_reactive_cache_key(*args))
+ if Feature.enabled?(:reactive_caching_check_key_exists, default_enabled: true)
+ Rails.cache.exist?(alive_reactive_cache_key(*args))
+ else
+ !!Rails.cache.read(alive_reactive_cache_key(*args))
+ end
end
def enqueuing_update(*args)
yield
- ensure
+
ReactiveCachingWorker.perform_in(self.class.reactive_cache_refresh_interval, self.class, id, *args)
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index a8f5642f726..b69cda4f2f9 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -85,11 +85,6 @@ class Deployment < ApplicationRecord
Commit.truncate_sha(sha)
end
- # Deprecated - will be replaced by a persisted cluster_id
- def deployment_platform_cluster
- environment.deployment_platform&.cluster
- end
-
def execute_hooks
deployment_data = Gitlab::DataBuilder::Deployment.build(self)
project.execute_services(deployment_data, :deployment_hooks)
@@ -176,45 +171,8 @@ class Deployment < ApplicationRecord
deployed_at&.to_time&.in_time_zone&.to_s(:medium)
end
- def has_metrics?
- success? && prometheus_adapter&.can_query?
- end
-
- def metrics
- return {} unless has_metrics?
-
- metrics = prometheus_adapter.query(:deployment, self)
- metrics&.merge(deployment_time: finished_at.to_i) || {}
- end
-
- def additional_metrics
- return {} unless has_metrics?
-
- metrics = prometheus_adapter.query(:additional_metrics_deployment, self)
- metrics&.merge(deployment_time: finished_at.to_i) || {}
- end
-
private
- def prometheus_adapter
- service = project.find_or_initialize_service('prometheus')
-
- if service.can_query?
- service
- else
- cluster_prometheus
- 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
- def cluster_prometheus
- cluster_with_fallback = cluster || deployment_platform_cluster
-
- cluster_with_fallback.application_prometheus if cluster_with_fallback&.application_prometheus_available?
- end
-
def ref_path
File.join(environment.ref_path, 'deployments', iid.to_s)
end
diff --git a/app/models/deployment_metrics.rb b/app/models/deployment_metrics.rb
new file mode 100644
index 00000000000..cfe762ca25e
--- /dev/null
+++ b/app/models/deployment_metrics.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+class DeploymentMetrics
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :project, :deployment
+
+ delegate :cluster, to: :deployment
+
+ def initialize(project, deployment)
+ @project = project
+ @deployment = deployment
+ end
+
+ def has_metrics?
+ deployment.success? && prometheus_adapter&.can_query?
+ end
+
+ def metrics
+ return {} unless has_metrics?
+
+ metrics = prometheus_adapter.query(:deployment, deployment)
+ metrics&.merge(deployment_time: deployment.finished_at.to_i) || {}
+ end
+
+ def additional_metrics
+ return {} unless has_metrics?
+
+ metrics = prometheus_adapter.query(:additional_metrics_deployment, deployment)
+ metrics&.merge(deployment_time: deployment.finished_at.to_i) || {}
+ end
+
+ private
+
+ def prometheus_adapter
+ strong_memoize(:prometheus_adapter) do
+ service = project.find_or_initialize_service('prometheus')
+
+ if service.can_query?
+ service
+ else
+ cluster_prometheus
+ end
+ 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
+ end
+end
diff --git a/app/models/environment_status.rb b/app/models/environment_status.rb
index 2fb6cadc8cd..465a42759df 100644
--- a/app/models/environment_status.rb
+++ b/app/models/environment_status.rb
@@ -3,11 +3,10 @@
class EnvironmentStatus
include Gitlab::Utils::StrongMemoize
- attr_reader :environment, :merge_request, :sha
+ attr_reader :project, :environment, :merge_request, :sha
delegate :id, to: :environment
delegate :name, to: :environment
- delegate :project, to: :environment
delegate :status, to: :deployment, allow_nil: true
delegate :deployed_at, to: :deployment, allow_nil: true
@@ -21,7 +20,8 @@ class EnvironmentStatus
build_environments_status(mr, user, mr.merge_pipeline)
end
- def initialize(environment, merge_request, sha)
+ def initialize(project, environment, merge_request, sha)
+ @project = project
@environment = environment
@merge_request = merge_request
@sha = sha
@@ -33,6 +33,12 @@ class EnvironmentStatus
end
end
+ def has_metrics?
+ strong_memoize(:has_metrics) do
+ deployment_metrics.has_metrics?
+ end
+ end
+
def changes
return [] if project.route_map_for(sha).nil?
@@ -48,6 +54,10 @@ class EnvironmentStatus
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
+ def deployment_metrics
+ @deployment_metrics ||= DeploymentMetrics.new(project, deployment)
+ end
+
def build_change(file)
public_path = project.public_path_for_source_path(file.new_path, sha)
return if public_path.nil?
@@ -67,7 +77,7 @@ class EnvironmentStatus
pipeline.environments.available.map do |environment|
next unless Ability.allowed?(user, :read_environment, environment)
- EnvironmentStatus.new(environment, mr, pipeline.sha)
+ EnvironmentStatus.new(pipeline.project, environment, mr, pipeline.sha)
end.compact
end
private_class_method :build_environments_status
diff --git a/app/models/group.rb b/app/models/group.rb
index 8e89c7ecfb1..9520db1bc0a 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -63,6 +63,8 @@ class Group < Namespace
after_save :update_two_factor_requirement
after_update :path_changed_hook, if: :saved_change_to_path?
+ scope :with_users, -> { includes(:users) }
+
class << self
def sort_by_attribute(method)
if method == 'storage_size_desc'
diff --git a/app/models/label_note.rb b/app/models/label_note.rb
index d6814f4a948..ba5f1f82a81 100644
--- a/app/models/label_note.rb
+++ b/app/models/label_note.rb
@@ -62,19 +62,27 @@ class LabelNote < Note
end
def note_text(html: false)
- added = labels_str('added', label_refs_by_action('add', html))
- removed = labels_str('removed', label_refs_by_action('remove', html))
+ added = labels_str(label_refs_by_action('add', html), prefix: 'added', suffix: added_suffix)
+ removed = labels_str(label_refs_by_action('remove', html), prefix: removed_prefix)
[added, removed].compact.join(' and ')
end
+ def removed_prefix
+ 'removed'
+ end
+
+ def added_suffix
+ ''
+ end
+
# returns string containing added/removed labels including
# count of deleted labels:
#
# added ~1 ~2 + 1 deleted label
# added 3 deleted labels
# added ~1 ~2 labels
- def labels_str(prefix, label_refs)
+ def labels_str(label_refs, prefix: '', suffix: '')
existing_refs = label_refs.select { |ref| ref.present? }.sort
refs_str = existing_refs.empty? ? nil : existing_refs.join(' ')
@@ -84,9 +92,9 @@ class LabelNote < Note
return unless refs_str || deleted_str
label_list_str = [refs_str, deleted_str].compact.join(' + ')
- suffix = 'label'.pluralize(deleted > 0 ? deleted : existing_refs.count)
+ suffix += ' label'.pluralize(deleted > 0 ? deleted : existing_refs.count)
- "#{prefix} #{label_list_str} #{suffix}"
+ "#{prefix} #{label_list_str} #{suffix.squish}"
end
def label_refs_by_action(action, html)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 8391d526d18..53977748c30 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -223,7 +223,13 @@ class MergeRequest < ApplicationRecord
end
def rebase_in_progress?
- strong_memoize(:rebase_in_progress) do
+ (rebase_jid.present? && Gitlab::SidekiqStatus.running?(rebase_jid)) ||
+ gitaly_rebase_in_progress?
+ end
+
+ # TODO: remove the Gitaly lookup after v12.1, when rebase_jid will be reliable
+ def gitaly_rebase_in_progress?
+ strong_memoize(:gitaly_rebase_in_progress) do
# The source project can be deleted
next false unless source_project
@@ -389,6 +395,26 @@ class MergeRequest < ApplicationRecord
update_column(:merge_jid, jid)
end
+ # Set off a rebase asynchronously, atomically updating the `rebase_jid` of
+ # the MR so that the status of the operation can be tracked.
+ def rebase_async(user_id)
+ transaction do
+ lock!
+
+ raise ActiveRecord::StaleObjectError if !open? || rebase_in_progress?
+
+ # Although there is a race between setting rebase_jid here and clearing it
+ # in the RebaseWorker, it can't do any harm since we check both that the
+ # attribute is set *and* that the sidekiq job is still running. So a JID
+ # for a completed RebaseWorker is equivalent to a nil JID.
+ jid = Sidekiq::Worker.skipping_transaction_check do
+ RebaseWorker.perform_async(id, user_id)
+ end
+
+ update_column(:rebase_jid, jid)
+ end
+ end
+
def merge_participants
participants = [author]
@@ -1101,6 +1127,19 @@ class MergeRequest < ApplicationRecord
"refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/merge"
end
+ def train_ref_path
+ "refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/train"
+ end
+
+ def cleanup_refs(only: :all)
+ target_refs = []
+ target_refs << ref_path if %i[all head].include?(only)
+ target_refs << merge_ref_path if %i[all merge].include?(only)
+ target_refs << train_ref_path if %i[all train].include?(only)
+
+ project.repository.delete_refs(*target_refs)
+ end
+
def self.merge_request_ref?(ref)
ref.start_with?("refs/#{Repository::REF_MERGE_REQUEST}/")
end
diff --git a/app/models/namespace/aggregation_schedule.rb b/app/models/namespace/aggregation_schedule.rb
index 355593597c6..0bef352cf24 100644
--- a/app/models/namespace/aggregation_schedule.rb
+++ b/app/models/namespace/aggregation_schedule.rb
@@ -44,4 +44,12 @@ class Namespace::AggregationSchedule < ApplicationRecord
def lease_key
"namespace:namespaces_root_statistics:#{namespace_id}"
end
+
+ # Used by ExclusiveLeaseGuard
+ # Overriding value as we never release the lease
+ # before the timeout in order to prevent multiple
+ # RootStatisticsWorker to start in a short span of time
+ def lease_release?
+ false
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 822def0f936..bfc35b77b8f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2145,7 +2145,7 @@ class Project < ApplicationRecord
public? &&
repository_exists? &&
Gitlab::CurrentSettings.hashed_storage_enabled &&
- Feature.enabled?(:object_pools, self)
+ Feature.enabled?(:object_pools, self, default_enabled: true)
end
def leave_pool_repository
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index dbdc8345c93..d2660051343 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -46,7 +46,7 @@ class DroneCiService < CiService
end
def commit_status(sha, ref)
- with_reactive_cache(sha, ref) {|cached| cached[:commit_status] }
+ with_reactive_cache(sha, ref) { |cached| cached[:commit_status] }
end
def calculate_reactive_cache(sha, ref)
@@ -68,7 +68,7 @@ class DroneCiService < CiService
end
{ commit_status: status }
- rescue Errno::ECONNREFUSED
+ rescue *Gitlab::HTTP::HTTP_ERRORS
{ commit_status: :error }
end
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 27b7827d55e..9f5c226f4c9 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -1,18 +1,8 @@
# frozen_string_literal: true
-##
-# NOTE:
-# We'll move this class to Clusters::Platforms::Kubernetes, which contains exactly the same logic.
-# After we've migrated data, we'll remove KubernetesService. This would happen in a few months.
-# If you're modyfiyng this class, please note that you should update the same change in Clusters::Platforms::Kubernetes.
class KubernetesService < Service
- include Gitlab::Kubernetes
- include ReactiveCaching
-
default_value_for :category, 'deployment'
- self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] }
-
# Namespace defaults to the project path, but can be overridden in case that
# is an invalid or inappropriate name
prop_accessor :namespace
@@ -47,8 +37,6 @@ class KubernetesService < Service
message: Gitlab::Regex.kubernetes_namespace_regex_message
}
- after_save :clear_reactive_cache!
-
def self.supported_events
%w()
end
@@ -94,72 +82,6 @@ class KubernetesService < Service
]
end
- def kubernetes_namespace_for(project)
- if namespace.present?
- namespace
- else
- default_namespace
- end
- end
-
- # Check we can connect to the Kubernetes API
- def test(*args)
- kubeclient = build_kube_client!
-
- kubeclient.core_client.discover
- { success: kubeclient.core_client.discovered, result: "Checked API discovery endpoint" }
- rescue => err
- { success: false, result: err }
- end
-
- # Project param was added on
- # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22011,
- # as a way to keep this service compatible with
- # Clusters::Platforms::Kubernetes, it won't be used on this method
- # as it's only needed for Clusters::Cluster.
- def predefined_variables(project:)
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables
- .append(key: 'KUBE_URL', value: api_url)
- .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true)
- .append(key: 'KUBE_NAMESPACE', value: kubernetes_namespace_for(project))
- .append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true)
-
- if ca_pem.present?
- variables
- .append(key: 'KUBE_CA_PEM', value: ca_pem)
- .append(key: 'KUBE_CA_PEM_FILE', value: ca_pem, file: true)
- end
- end
- end
-
- # Constructs a list of terminals from the reactive cache
- #
- # Returns nil if the cache is empty, in which case you should try again a
- # short time later
- def terminals(environment)
- with_reactive_cache do |data|
- project = environment.project
-
- pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
- terminals = pods.flat_map { |pod| terminals_for_pod(api_url, kubernetes_namespace_for(project), pod) }.compact
- terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
- end
- end
-
- # Caches resources in the namespace so other calls don't need to block on
- # network access
- def calculate_reactive_cache
- return unless active? && project && !project.pending_delete?
-
- # We may want to cache extra things in the future
- { pods: read_pods }
- end
-
- def kubeclient
- @kubeclient ||= build_kube_client!
- end
-
def deprecated?
true
end
@@ -186,14 +108,6 @@ class KubernetesService < Service
private
- def kubeconfig
- to_kubeconfig(
- url: api_url,
- namespace: kubernetes_namespace_for(project),
- token: token,
- ca_pem: ca_pem)
- end
-
def namespace_placeholder
default_namespace || TEMPLATE_PLACEHOLDER
end
@@ -205,49 +119,6 @@ class KubernetesService < Service
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
- def build_kube_client!
- raise "Incomplete settings" unless api_url && kubernetes_namespace_for(project) && token
-
- Gitlab::Kubernetes::KubeClient.new(
- api_url,
- auth_options: kubeclient_auth_options,
- ssl_options: kubeclient_ssl_options,
- http_proxy_uri: ENV['http_proxy']
- )
- end
-
- # Returns a hash of all pods in the namespace
- def read_pods
- kubeclient = build_kube_client!
-
- kubeclient.get_pods(namespace: kubernetes_namespace_for(project)).as_json
- rescue Kubeclient::ResourceNotFoundError
- []
- end
-
- def kubeclient_ssl_options
- opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
-
- if ca_pem.present?
- opts[:cert_store] = OpenSSL::X509::Store.new
- opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
- end
-
- opts
- end
-
- def kubeclient_auth_options
- { bearer_token: token }
- end
-
- def terminal_auth
- {
- token: token,
- ca_pem: ca_pem,
- max_session_time: Gitlab::CurrentSettings.terminal_max_session_time
- }
- end
-
def enforce_namespace_to_lower_case
self.namespace = self.namespace&.downcase
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 8a179b4d56d..3802d258664 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProjectStatistics < ApplicationRecord
+ include AfterCommitQueue
+
belongs_to :project
belongs_to :namespace
@@ -15,6 +17,7 @@ class ProjectStatistics < ApplicationRecord
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count].freeze
INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size] }.freeze
+ NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size].freeze
scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) }
@@ -22,13 +25,17 @@ class ProjectStatistics < ApplicationRecord
repository_size + lfs_objects_size
end
- def refresh!(only: nil)
+ def refresh!(only: [])
COLUMNS_TO_REFRESH.each do |column, generator|
- if only.blank? || only.include?(column)
+ if only.empty? || only.include?(column)
public_send("update_#{column}") # rubocop:disable GitlabSecurity/PublicSend
end
end
+ if only.empty? || only.any? { |column| NAMESPACE_RELATABLE_COLUMNS.include?(column) }
+ schedule_namespace_aggregation_worker
+ end
+
save!
end
@@ -81,4 +88,18 @@ class ProjectStatistics < ApplicationRecord
update_all(updates.join(', '))
end
+
+ private
+
+ def schedule_namespace_aggregation_worker
+ run_after_commit do
+ next unless schedule_aggregation_worker?
+
+ Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id)
+ end
+ end
+
+ def schedule_aggregation_worker?
+ Feature.enabled?(:update_statistics_namespace, project&.root_ancestor)
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index d087a5a7bbd..a25d5abfa64 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -839,10 +839,14 @@ class Repository
end
end
- def merge_to_ref(user, source_sha, merge_request, target_ref, message)
+ def merge_to_ref(user, source_sha, merge_request, target_ref, message, first_parent_ref)
branch = merge_request.target_branch
- raw.merge_to_ref(user, source_sha, branch, target_ref, message)
+ raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
+ end
+
+ def delete_refs(*ref_names)
+ raw.delete_refs(*ref_names)
end
def ff_merge(user, source, target_branch, merge_request: nil)
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
index f2c7cb6a65d..ad08f4763ae 100644
--- a/app/models/resource_label_event.rb
+++ b/app/models/resource_label_event.rb
@@ -36,10 +36,9 @@ class ResourceLabelEvent < ApplicationRecord
issue || merge_request
end
- # create same discussion id for all actions with the same user and time
def discussion_id(resource = nil)
strong_memoize(:discussion_id) do
- Digest::SHA1.hexdigest([self.class.name, created_at, user_id].join("-"))
+ Digest::SHA1.hexdigest(discussion_id_key.join("-"))
end
end
@@ -121,4 +120,8 @@ class ResourceLabelEvent < ApplicationRecord
def resource_parent
issuable.project || issuable.group
end
+
+ def discussion_id_key
+ [self.class.name, created_at, user_id]
+ end
end
diff --git a/app/serializers/environment_status_entity.rb b/app/serializers/environment_status_entity.rb
index f6321b9e520..811cc2ad5af 100644
--- a/app/serializers/environment_status_entity.rb
+++ b/app/serializers/environment_status_entity.rb
@@ -11,7 +11,7 @@ class EnvironmentStatusEntity < Grape::Entity
project_environment_path(es.project, es.environment)
end
- expose :metrics_url, if: ->(*) { can_read_environment? && deployment.has_metrics? } do |es|
+ expose :metrics_url, if: ->(*) { can_read_environment? && has_metrics? } do |es|
metrics_project_environment_deployment_path(es.project, es.environment, es.deployment)
end
@@ -45,8 +45,8 @@ class EnvironmentStatusEntity < Grape::Entity
object.environment
end
- def deployment
- object.deployment
+ def has_metrics?
+ object.has_metrics?
end
def project
diff --git a/app/serializers/test_suite_comparer_entity.rb b/app/serializers/test_suite_comparer_entity.rb
index 9fa3a897ebe..d402a4d5718 100644
--- a/app/serializers/test_suite_comparer_entity.rb
+++ b/app/serializers/test_suite_comparer_entity.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
class TestSuiteComparerEntity < Grape::Entity
+ DEFAULT_MAX_TESTS = 100
+ DEFAULT_MIN_TESTS = 10
+
expose :name
expose :total_status, as: :status
@@ -10,7 +13,27 @@ class TestSuiteComparerEntity < Grape::Entity
expose :failed_count, as: :failed
end
- expose :new_failures, using: TestCaseEntity
- expose :resolved_failures, using: TestCaseEntity
- expose :existing_failures, using: TestCaseEntity
+ # rubocop: disable CodeReuse/ActiveRecord
+ expose :new_failures, using: TestCaseEntity do |suite|
+ suite.new_failures.take(max_tests)
+ end
+
+ expose :existing_failures, using: TestCaseEntity do |suite|
+ suite.existing_failures.take(
+ max_tests(suite.new_failures))
+ end
+
+ expose :resolved_failures, using: TestCaseEntity do |suite|
+ suite.resolved_failures.take(
+ max_tests(suite.new_failures, suite.existing_failures))
+ end
+
+ private
+
+ def max_tests(*used)
+ return Integer::MAX unless Feature.enabled?(:ci_limit_test_reports_size, default_enabled: true)
+
+ [DEFAULT_MAX_TESTS - used.map(&:count).sum, DEFAULT_MIN_TESTS].max
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/services/auto_merge/base_service.rb b/app/services/auto_merge/base_service.rb
index d726085b89a..e06659a39cd 100644
--- a/app/services/auto_merge/base_service.rb
+++ b/app/services/auto_merge/base_service.rb
@@ -29,7 +29,7 @@ module AutoMerge
end
def cancel(merge_request)
- if cancel_auto_merge(merge_request)
+ if clear_auto_merge_parameters(merge_request)
yield if block_given?
success
@@ -38,6 +38,16 @@ module AutoMerge
end
end
+ def abort(merge_request, reason)
+ if clear_auto_merge_parameters(merge_request)
+ yield if block_given?
+
+ success
+ else
+ error("Can't abort the automatic merge", 406)
+ end
+ end
+
private
def strategy
@@ -46,7 +56,7 @@ module AutoMerge
end
end
- def cancel_auto_merge(merge_request)
+ def clear_auto_merge_parameters(merge_request)
merge_request.auto_merge_enabled = false
merge_request.merge_user = nil
diff --git a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
index c41073a73e9..6a33ec071db 100644
--- a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb
@@ -5,7 +5,7 @@ module AutoMerge
def execute(merge_request)
super do
if merge_request.saved_change_to_auto_merge_enabled?
- SystemNoteService.merge_when_pipeline_succeeds(merge_request, project, current_user, merge_request.diff_head_commit)
+ SystemNoteService.merge_when_pipeline_succeeds(merge_request, project, current_user, merge_request.actual_head_pipeline.sha)
end
end
end
@@ -19,7 +19,13 @@ module AutoMerge
def cancel(merge_request)
super do
- SystemNoteService.cancel_merge_when_pipeline_succeeds(merge_request, @project, @current_user)
+ SystemNoteService.cancel_merge_when_pipeline_succeeds(merge_request, project, current_user)
+ end
+ end
+
+ def abort(merge_request, reason)
+ super do
+ SystemNoteService.abort_merge_when_pipeline_succeeds(merge_request, project, current_user, reason)
end
end
diff --git a/app/services/auto_merge_service.rb b/app/services/auto_merge_service.rb
index 926d2f5fc66..95bf2db2018 100644
--- a/app/services/auto_merge_service.rb
+++ b/app/services/auto_merge_service.rb
@@ -42,6 +42,12 @@ class AutoMergeService < BaseService
get_service_instance(merge_request.auto_merge_strategy).cancel(merge_request)
end
+ def abort(merge_request, reason)
+ return error("Can't abort the automatic merge", 406) unless merge_request.auto_merge_enabled?
+
+ get_service_instance(merge_request.auto_merge_strategy).abort(merge_request, reason)
+ end
+
def available_strategies(merge_request)
self.class.all_strategies.select do |strategy|
get_service_instance(strategy).available_for?(merge_request)
diff --git a/app/services/ci/compare_reports_base_service.rb b/app/services/ci/compare_reports_base_service.rb
index d5625857599..6c2d80d8f45 100644
--- a/app/services/ci/compare_reports_base_service.rb
+++ b/app/services/ci/compare_reports_base_service.rb
@@ -8,7 +8,7 @@ module Ci
status: :parsed,
key: key(base_pipeline, head_pipeline),
data: serializer_class
- .new(project: project)
+ .new(**serializer_params)
.represent(comparer).as_json
}
rescue Gitlab::Ci::Parsers::ParserError => e
@@ -40,6 +40,10 @@ module Ci
raise NotImplementedError
end
+ def serializer_params
+ { project: project }
+ end
+
def get_report(pipeline)
raise NotImplementedError
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index c17712355af..cdcc4b15bea 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -65,7 +65,7 @@ module Ci
def execute!(*args, &block)
execute(*args, &block).tap do |pipeline|
unless pipeline.persisted?
- raise CreateError, pipeline.errors.full_messages.join(',')
+ raise CreateError, pipeline.error_messages
end
end
end
diff --git a/app/services/discussions/update_diff_position_service.rb b/app/services/discussions/update_diff_position_service.rb
index c61437fb2e3..7bdf7711155 100644
--- a/app/services/discussions/update_diff_position_service.rb
+++ b/app/services/discussions/update_diff_position_service.rb
@@ -3,7 +3,8 @@
module Discussions
class UpdateDiffPositionService < BaseService
def execute(discussion)
- result = tracer.trace(discussion.position)
+ old_position = discussion.position
+ result = tracer.trace(old_position)
return unless result
position = result[:position]
diff --git a/app/services/issuable/bulk_update_service.rb b/app/services/issuable/bulk_update_service.rb
index c4beddf2294..6d215d7a3b9 100644
--- a/app/services/issuable/bulk_update_service.rb
+++ b/app/services/issuable/bulk_update_service.rb
@@ -1,7 +1,15 @@
# frozen_string_literal: true
module Issuable
- class BulkUpdateService < IssuableBaseService
+ class BulkUpdateService
+ include Gitlab::Allowable
+
+ attr_accessor :current_user, :params
+
+ def initialize(user = nil, params = {})
+ @current_user, @params = user, params.dup
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def execute(type)
model_class = type.classify.constantize
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 02de080e0ba..db673cace81 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -182,7 +182,7 @@ class IssuableBaseService < BaseService
# To be overridden by subclasses
end
- def before_update(issuable)
+ def before_update(issuable, skip_spam_check: false)
# To be overridden by subclasses
end
@@ -257,7 +257,7 @@ class IssuableBaseService < BaseService
last_edited_at: Time.now,
last_edited_by: current_user))
- before_update(issuable)
+ before_update(issuable, skip_spam_check: true)
if issuable.with_transaction_returning_status { issuable.save }
# We do not touch as it will affect a update on updated_at field
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 6b9f23f24cd..7cd825aa967 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -17,8 +17,8 @@ module Issues
super
end
- def before_update(issue)
- spam_check(issue, current_user)
+ def before_update(issue, skip_spam_check: false)
+ spam_check(issue, current_user) unless skip_spam_check
end
def handle_changes(issue, options)
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index c34fbeb2adb..067510a8a0a 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -68,8 +68,8 @@ module MergeRequests
!merge_request.for_fork?
end
- def cancel_auto_merge(merge_request)
- AutoMergeService.new(project, current_user).cancel(merge_request)
+ def abort_auto_merge(merge_request, reason)
+ AutoMergeService.new(project, current_user).abort(merge_request, reason)
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index b81a4dd81d2..c2174d2a130 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -18,7 +18,7 @@ module MergeRequests
invalidate_cache_counts(merge_request, users: merge_request.assignees)
merge_request.update_project_counter_caches
cleanup_environments(merge_request)
- cancel_auto_merge(merge_request)
+ abort_auto_merge(merge_request, 'merge request was closed')
end
merge_request
diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb
index efe4dcd6255..0ea50a5dbf5 100644
--- a/app/services/merge_requests/merge_to_ref_service.rb
+++ b/app/services/merge_requests/merge_to_ref_service.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module MergeRequests
- # Performs the merge between source SHA and the target branch. Instead
+ # Performs the merge between source SHA and the target branch or the specified first parent ref. Instead
# of writing the result to the MR target branch, it targets the `target_ref`.
#
# Ideally this should leave the `target_ref` state with the same state the
@@ -56,12 +56,22 @@ module MergeRequests
raise_error(error) if error
end
+ ##
+ # The parameter `target_ref` is where the merge result will be written.
+ # Default is the merge ref i.e. `refs/merge-requests/:iid/merge`.
def target_ref
- merge_request.merge_ref_path
+ params[:target_ref] || merge_request.merge_ref_path
+ end
+
+ ##
+ # The parameter `first_parent_ref` is the main line of the merge commit.
+ # Default is the target branch ref of the merge request.
+ def first_parent_ref
+ params[:first_parent_ref] || merge_request.target_branch_ref
end
def commit
- repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message)
+ repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref)
rescue Gitlab::Git::PreReceiveError => error
raise MergeError, error.message
end
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index 4b9921c28ba..8d3b9b05819 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -15,7 +15,7 @@ module MergeRequests
end
def rebase
- if merge_request.rebase_in_progress?
+ if merge_request.gitaly_rebase_in_progress?
log_error('Rebase task canceled: Another rebase is already in progress', save_message_on_model: true)
return false
end
@@ -27,6 +27,8 @@ module MergeRequests
log_error(REBASE_ERROR, save_message_on_model: true)
log_error(e.message)
false
+ ensure
+ merge_request.update_column(:rebase_jid, nil)
end
end
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 4b199bd8fa8..8961d2e1023 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -24,7 +24,7 @@ module MergeRequests
reload_merge_requests
outdate_suggestions
refresh_pipelines_on_merge_requests
- cancel_auto_merges
+ abort_auto_merges
mark_pending_todos_done
cache_merge_requests_closing_issues
@@ -142,9 +142,9 @@ module MergeRequests
end
end
- def cancel_auto_merges
+ def abort_auto_merges
merge_requests_for_source_branch.each do |merge_request|
- cancel_auto_merge(merge_request)
+ abort_auto_merge(merge_request, 'source branch was updated')
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 0066cd0491f..d361e96babf 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -44,7 +44,7 @@ module MergeRequests
merge_request.previous_changes['target_branch'].first,
merge_request.target_branch)
- cancel_auto_merge(merge_request)
+ abort_auto_merge(merge_request, 'target branch was changed')
end
if merge_request.assignees != old_assignees
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
index 3be958e1613..399f4c35d66 100644
--- a/app/services/prometheus/adapter_service.rb
+++ b/app/services/prometheus/adapter_service.rb
@@ -27,12 +27,9 @@ module Prometheus
end
def cluster_prometheus_adapter
- return unless deployment_platform.respond_to?(:cluster)
+ application = deployment_platform&.cluster&.application_prometheus
- cluster = deployment_platform.cluster
- return unless cluster.application_prometheus&.available?
-
- cluster.application_prometheus
+ application if application&.available?
end
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 237ddbcf2c2..e4564bc9b00 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -221,8 +221,8 @@ module SystemNoteService
end
# Called when 'merge when pipeline succeeds' is executed
- def merge_when_pipeline_succeeds(noteable, project, author, last_commit)
- body = "enabled an automatic merge when the pipeline for #{last_commit.to_reference(project)} succeeds"
+ def merge_when_pipeline_succeeds(noteable, project, author, sha)
+ body = "enabled an automatic merge when the pipeline for #{sha} succeeds"
create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
end
@@ -234,6 +234,16 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
end
+ # Called when 'merge when pipeline succeeds' is aborted
+ def abort_merge_when_pipeline_succeeds(noteable, project, author, reason)
+ body = "aborted the automatic merge because #{reason}"
+
+ ##
+ # TODO: Abort message should be sent by the system, not a particular user.
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/63187.
+ create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
+ end
+
def handle_merge_request_wip(noteable, project, author)
prefix = noteable.work_in_progress? ? "marked" : "unmarked"
@@ -268,11 +278,13 @@ module SystemNoteService
merge_request = discussion.noteable
diff_refs = change_position.diff_refs
version_index = merge_request.merge_request_diffs.viewable.count
+ position_on_text = change_position.on_text?
+ text_parts = ["changed this #{position_on_text ? 'line' : 'file'} in"]
- text_parts = ["changed this line in"]
if version_params = merge_request.version_params_for(diff_refs)
- line_code = change_position.line_code(project.repository)
- url = url_helpers.diffs_project_merge_request_path(project, merge_request, version_params.merge(anchor: line_code))
+ repository = project.repository
+ anchor = position_on_text ? change_position.line_code(repository) : change_position.file_hash
+ url = url_helpers.diffs_project_merge_request_path(project, merge_request, version_params.merge(anchor: anchor))
text_parts << "[version #{version_index} of the diff](#{url})"
else
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 1c7582533ad..b326b266017 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -203,6 +203,6 @@ class FileUploader < GitlabUploader
end
def secure_url
- File.join('/uploads', @secret, file.filename)
+ File.join('/uploads', @secret, filename)
end
end
diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb
index b43162f0935..1ac69601d18 100644
--- a/app/uploaders/personal_file_uploader.rb
+++ b/app/uploaders/personal_file_uploader.rb
@@ -93,6 +93,6 @@ class PersonalFileUploader < FileUploader
end
def secure_url
- File.join('/', base_dir, secret, file.filename)
+ File.join('/', base_dir, secret, filename)
end
end
diff --git a/app/views/admin/application_settings/_grafana.html.haml b/app/views/admin/application_settings/_grafana.html.haml
new file mode 100644
index 00000000000..b6e02bde895
--- /dev/null
+++ b/app/views/admin/application_settings/_grafana.html.haml
@@ -0,0 +1,17 @@
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ %p
+ = _("Add a Grafana button in the admin sidebar, monitoring section, to access a variety of statistics on the health and performance of GitLab.")
+ = link_to icon('question-circle'), help_page_path('administration/monitoring/performance/grafana_configuration.md')
+ .form-group
+ .form-check
+ = f.check_box :grafana_enabled, class: 'form-check-input'
+ = f.label :grafana_enabled, class: 'form-check-label' do
+ = _('Enable access to Grafana')
+ .form-group
+ = f.label :grafana_url, _('Grafana URL'), class: 'label-bold'
+ = f.text_field :grafana_url, class: 'form-control', placeholder: '/-/grafana'
+
+ = f.submit _('Save changes'), class: "btn btn-success"
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index 01d61beaf53..55a48da8342 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -24,6 +24,17 @@
.settings-content
= render 'prometheus'
+%section.settings.as-grafana.no-animate#js-grafana-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Metrics - Grafana')
+ %button.btn.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Enable and configure Grafana.')
+ .settings-content
+ = render 'grafana'
+
%section.settings.qa-performance-bar-settings.as-performance-bar.no-animate#js-performance-bar-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index f524d35d79e..98230684d56 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -43,11 +43,7 @@
= render_if_exists 'admin/namespace_plan_info', namespace: @group
%li
- %span.light= _('Storage:')
- %strong= storage_counter(@group.storage_size)
- (
- = storage_counters_details(@group)
- )
+ = render 'shared/storage_counter_statistics', storage_size: @group.storage_size, storage_details: @group
%li
%span.light= _('Group Git LFS status:')
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index e23accc1ea9..0fae8060b32 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -73,11 +73,7 @@
= @project.repository.relative_path
%li
- %span.light= _('Storage:')
- %strong= storage_counter(@project.statistics&.storage_size)
- - if @project.statistics
- = surround '(', ')' do
- = storage_counters_details(@project.statistics)
+ = render 'shared/storage_counter_statistics', storage_size: @project.statistics&.storage_size, storage_details: @project.statistics
%li
%span.light last commit:
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 5c6131db37d..a988f746ced 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -139,7 +139,7 @@
%strong
= link_to @user.created_by.name, [:admin, @user.created_by]
- = render_if_exists partial: "namespaces/shared_runner_status", locals: { namespace: @user.namespace }
+ = render_if_exists 'namespaces/shared_runner_status', namespace: @user.namespace
.col-md-6
- unless @user == current_user
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index db6e40a6fd0..8cdfc7369a0 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -49,5 +49,5 @@
- else
.todo-actions
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do
- Add todo
+ Add a To Do
= icon('spinner spin')
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 8212fb8bb33..731e763f2be 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -1,11 +1,11 @@
- @hide_top_links = true
-- page_title "Todos"
-- header_title "Todos", dashboard_todos_path
+- page_title "To-Do List"
+- header_title "To-Do List", dashboard_todos_path
= render_dashboard_gold_trial(current_user)
.page-title-holder.d-flex.align-items-center
- %h1.page-title= _('Todos')
+ %h1.page-title= _('To-Do List')
- if current_user.todos.any?
.top-area
@@ -13,7 +13,7 @@
%li.todos-pending{ class: active_when(params[:state].blank? || params[:state] == 'pending') }>
= link_to todos_filter_path(state: 'pending') do
%span
- Todos
+ To Do
%span.badge.badge-pill
= number_with_delimiter(todos_pending_count)
%li.todos-done{ class: active_when(params[:state] == 'done') }>
@@ -102,24 +102,24 @@
%p
Are you looking for things to do? Take a look at
= succeed "," do
- = link_to "the opened issues", issues_dashboard_path
+ = link_to "open issues", issues_dashboard_path
contribute to
- = link_to "merge requests", merge_requests_dashboard_path
- or mention someone in a comment to assign a new todo automatically.
+ = link_to "a merge request\,", merge_requests_dashboard_path
+ or mention someone in a comment to automatically assign them a new to-do item.
- else
%h4.text-center
- There are no todos to show.
+ Nothing is on your to-do list. Nice work!
- else
.todos-empty
.todos-empty-hero.svg-content
= image_tag 'illustrations/todos_empty.svg'
.todos-empty-content
%h4
- Todos let you see what you should do next
+ Your To-Do List shows what to work on next
%p
- When an issue or merge request is assigned to you, or when you
+ When an issue or merge request is assigned to you, or when you receive a
%strong
@mention
- in a comment, this will trigger a new item in your todo list, automatically.
+ in a comment, this automatically triggers a new item in your To-Do List.
%p
- You will always know what to work on next.
+ It's how you always know what to work on next.
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 91d17cfd745..f05e269553a 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,3 +1,5 @@
+- @can_bulk_update = can?(current_user, :admin_issue, @group)
+
- page_title "Issues"
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues")
@@ -9,8 +11,15 @@
= render 'shared/issuable/nav', type: :issues
.nav-controls
= render 'shared/issuable/feed_buttons'
+
+ - if @can_bulk_update
+ = render_if_exists 'shared/issuable/bulk_update_button'
+
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", type: :issues, with_feature_enabled: 'issues', with_shared: false, include_projects_in_subgroups: true
= render 'shared/issuable/search_bar', type: :issues
+ - if @can_bulk_update
+ = render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :issues
+
= render 'shared/issues'
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 496ec3c78b0..a5f57f5893c 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,5 +1,5 @@
- if @group && @group.persisted? && @group.path
- - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) }
+ - group_data_attrs = { group_path: j(@group.path), name: j(@group.name), issues_path: issues_group_path(@group), mr_path: merge_requests_group_path(@group) }
- if @project && @project.persisted?
- project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: project_issues_path(@project), mr_path: project_merge_requests_path(@project), issues_disabled: !@project.issues_enabled? }
.search.search-form{ data: { track_label: "navbar_search", track_event: "activate_form_input" } }
diff --git a/app/views/layouts/fullscreen.html.haml b/app/views/layouts/fullscreen.html.haml
index fa04b5be9f2..91a7777514c 100644
--- a/app/views/layouts/fullscreen.html.haml
+++ b/app/views/layouts/fullscreen.html.haml
@@ -3,6 +3,7 @@
= render "layouts/head"
%body{ class: "#{user_application_theme} #{@body_class} fullscreen-layout", data: { page: body_data_page } }
= render 'peek/bar'
+ = header_message
= render partial: "layouts/header/default", locals: { project: @project, group: @group }
= render 'shared/outdated_browser'
.mobile-overlay
@@ -12,3 +13,4 @@
= render "layouts/flash"
.content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch mt-0" }
= yield
+ = footer_message
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index f8b7d0c530a..f9ee6f42e23 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -53,7 +53,7 @@
= number_with_delimiter(merge_requests_count)
- if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
- = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _('Todos') }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('todo-done', size: 16)
%span.badge.badge-pill.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
= todos_count_format(todos_pending_count)
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 83fe871285a..87133c7ba22 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -81,6 +81,11 @@
= link_to admin_requests_profiles_path, title: _('Requests Profiles') do
%span
= _('Requests Profiles')
+ - if Gitlab::CurrentSettings.current_application_settings.grafana_enabled?
+ = nav_link do
+ = link_to Gitlab::CurrentSettings.current_application_settings.grafana_url, target: '_blank', title: _('Metrics Dashboard') do
+ %span
+ = _('Metrics Dashboard')
= render_if_exists 'layouts/nav/ee/admin/new_monitoring_sidebar'
= nav_link(controller: :broadcast_messages) do
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 4ebfaff0860..d16e2dddbe0 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -92,21 +92,21 @@
.col-lg-8
.form-group
%h5= s_('Preferences|Time format')
- .checkbox-icon-inline-wrapper.form-check
+ .checkbox-icon-inline-wrapper
- time_format_label = capture do
= s_('Preferences|Display time in 24-hour format')
- = f.check_box :time_format_in_24h, class: 'form-check-input'
+ = f.check_box :time_format_in_24h
= f.label :time_format_in_24h do
= time_format_label
%h5= s_('Preferences|Time display')
- .checkbox-icon-inline-wrapper.form-check
+ .checkbox-icon-inline-wrapper
- time_display_label = capture do
= s_('Preferences|Use relative times')
- = f.check_box :time_display_relative, class: 'form-check-input'
+ = f.check_box :time_display_relative
= f.label :time_display_relative do
= time_display_label
- .text-muted
- = s_('Preferences|For example: 30 mins ago.')
+ .form-text.text-muted
+ = s_('Preferences|For example: 30 mins ago.')
.col-lg-4.profile-settings-sidebar
.col-lg-8
.form-group
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index b2dab0b5348..d95045c9cce 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -5,6 +5,7 @@
- if current_user && can?(current_user, :download_code, project)
= render 'shared/no_ssh'
= render 'shared/no_password'
+ = render_if_exists 'shared/shared_runners_minutes_limit', project: project
- unless project.empty_repo?
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
= render_if_exists 'projects/above_size_limit_warning', project: project
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 52bb797b5b3..8d3e54dc455 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -3,13 +3,14 @@
- data_action = can_create_merge_request ? 'create-mr' : 'create-branch'
- value = can_create_merge_request ? 'Create merge request' : 'Create branch'
- value = can_create_confidential_merge_request? ? _('Create confidential merge request') : value
+ - create_mr_text = can_create_confidential_merge_request? ? _('Create confidential merge request') : _('Create merge request')
- can_create_path = can_create_branch_project_issue_path(@project, @issue)
- create_mr_path = create_merge_request_project_issue_path(@project, @issue, branch_name: @issue.to_branch_name, ref: @project.default_branch)
- create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid)
- refs_path = refs_namespace_project_path(@project.namespace, @project, search: '')
- .create-mr-dropdown-wrap.d-inline-block.full-width-mobile{ data: { can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path } }
+ .create-mr-dropdown-wrap.d-inline-block.full-width-mobile.js-create-mr{ data: { project_path: @project.full_path, project_id: @project.id, can_create_path: can_create_path, create_mr_path: create_mr_path, create_branch_path: create_branch_path, refs_path: refs_path, is_confidential: can_create_confidential_merge_request?.to_s } }
.btn-group.btn-group-sm.unavailable
%button.btn.btn-grouped{ type: 'button', disabled: 'disabled' }
= icon('spinner', class: 'fa-spin')
@@ -26,7 +27,7 @@
.droplab-dropdown
%ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-right.gl-show-field-errors{ class: ("create-confidential-merge-request-dropdown-menu" if can_create_confidential_merge_request?), data: { dropdown: true } }
- if can_create_merge_request
- %li.droplab-item-selected{ role: 'button', data: { value: 'create-mr', text: _('Create merge request') } }
+ %li.droplab-item-selected{ role: 'button', data: { value: 'create-mr', text: create_mr_text } }
.menu-item
= icon('check', class: 'icon')
- if can_create_confidential_merge_request?
@@ -41,6 +42,8 @@
%li.divider.droplab-item-ignore
%li.droplab-item-ignore.prepend-left-8.append-right-8.prepend-top-16
+ - if can_create_confidential_merge_request?
+ #js-forked-project{ data: { namespace_path: @project.namespace.full_path, project_path: @project.full_path, new_fork_path: new_project_fork_path(@project), help_page_path: help_page_path('user/project/merge_requests') } }
.form-group
%label{ for: 'new-branch-name' }
= _('Branch name')
@@ -55,4 +58,8 @@
.form-group
%button.btn.btn-success.js-create-target{ type: 'button', data: { action: 'create-mr' } }
- = _('Create merge request')
+ = create_mr_text
+
+ - if can_create_confidential_merge_request?
+ %p.text-warning.js-exposed-info-warning.hidden
+ = _('This may expose confidential information as the selected fork is in another namespace that can have other members.')
diff --git a/app/views/shared/_storage_counter_statistics.html.haml b/app/views/shared/_storage_counter_statistics.html.haml
new file mode 100644
index 00000000000..99b2323ca82
--- /dev/null
+++ b/app/views/shared/_storage_counter_statistics.html.haml
@@ -0,0 +1,4 @@
+%span.light= _('Storage:')
+%strong= storage_counter(storage_size)
+- if storage_details
+ (#{storage_counters_details(storage_details)})
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 1bd56e064d5..214e87052da 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -17,8 +17,7 @@
= render 'shared/issuable/form/template_selector', issuable: issuable
= render 'shared/issuable/form/title', issuable: issuable, form: form, has_wip_commits: commits && commits.detect(&:work_in_progress?)
-- if Gitlab::Graphql.enabled?
- #js-suggestions{ data: { project_path: @project.full_path } }
+#js-suggestions{ data: { project_path: @project.full_path } }
= render 'shared/form_elements/description', model: issuable, form: form, project: project
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index e87e560266f..b4f8377c008 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -10,7 +10,7 @@
.block.issuable-sidebar-header
- if signed_in
%span.issuable-header-text.hide-collapsed.float-left
- = _('Todo')
+ = _('To Do')
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => "Toggle sidebar", title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon
- if signed_in
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index 25c3a945077..2b36ccb8304 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'sidekiq/api'
+
Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker
@@ -44,6 +46,10 @@ module ApplicationWorker
get_sidekiq_options['queue'].to_s
end
+ def queue_size
+ Sidekiq::Queue.new(queue).size
+ end
+
def bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
end
diff --git a/app/workers/rebase_worker.rb b/app/workers/rebase_worker.rb
index a6baebc1443..8d06adcd993 100644
--- a/app/workers/rebase_worker.rb
+++ b/app/workers/rebase_worker.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# The RebaseWorker must be wrapped in important concurrency code, so should only
+# be scheduled via MergeRequest#rebase_async
class RebaseWorker
include ApplicationWorker
diff --git a/changelogs/unreleased/12533-shared-runners-warning.yml b/changelogs/unreleased/12533-shared-runners-warning.yml
new file mode 100644
index 00000000000..b2b526cc2e4
--- /dev/null
+++ b/changelogs/unreleased/12533-shared-runners-warning.yml
@@ -0,0 +1,5 @@
+---
+title: Removes EE differences for app/views/admin/users/show.html.haml
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/12550-fullscrean.yml b/changelogs/unreleased/12550-fullscrean.yml
new file mode 100644
index 00000000000..f20b191f411
--- /dev/null
+++ b/changelogs/unreleased/12550-fullscrean.yml
@@ -0,0 +1,5 @@
+---
+title: Removes EE differences for app/views/layouts/fullscreen.html.haml
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/12553-preferences.yml b/changelogs/unreleased/12553-preferences.yml
new file mode 100644
index 00000000000..c41a8c98e4e
--- /dev/null
+++ b/changelogs/unreleased/12553-preferences.yml
@@ -0,0 +1,5 @@
+---
+title: Removes EE diff for app/views/profiles/preferences/show.html.haml
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/40379-CJK-search-min-chars.yml b/changelogs/unreleased/40379-CJK-search-min-chars.yml
new file mode 100644
index 00000000000..6f6c4df464f
--- /dev/null
+++ b/changelogs/unreleased/40379-CJK-search-min-chars.yml
@@ -0,0 +1,5 @@
+---
+title: Remove minimum character limits for fuzzy searches when using a CTE
+merge_request: 29810
+author:
+type: fixed
diff --git a/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml b/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml
new file mode 100644
index 00000000000..908a132688c
--- /dev/null
+++ b/changelogs/unreleased/51794-add-ordering-to-runner-jobs-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add order_by and sort params to list runner jobs api
+merge_request: 29629
+author: Sujay Patel
+type: added
diff --git a/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml b/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml
new file mode 100644
index 00000000000..c11d74bd4fe
--- /dev/null
+++ b/changelogs/unreleased/53357-fix-plus-in-upload-file-names.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken URLs for uploads with a plus in the filename
+merge_request: 29915
+author:
+type: fixed
diff --git a/changelogs/unreleased/54117-transactional-rebase.yml b/changelogs/unreleased/54117-transactional-rebase.yml
new file mode 100644
index 00000000000..d0c93114c49
--- /dev/null
+++ b/changelogs/unreleased/54117-transactional-rebase.yml
@@ -0,0 +1,5 @@
+---
+title: Allow asynchronous rebase operations to be monitored
+merge_request: 29940
+author:
+type: fixed
diff --git a/changelogs/unreleased/57793-fix-line-age.yml b/changelogs/unreleased/57793-fix-line-age.yml
new file mode 100644
index 00000000000..cf4e328e54a
--- /dev/null
+++ b/changelogs/unreleased/57793-fix-line-age.yml
@@ -0,0 +1,5 @@
+---
+title: Support note position tracing on an image
+merge_request: 30158
+author:
+type: fixed
diff --git a/changelogs/unreleased/60856-deleting-binary-file.yml b/changelogs/unreleased/60856-deleting-binary-file.yml
new file mode 100644
index 00000000000..9b587ed591b
--- /dev/null
+++ b/changelogs/unreleased/60856-deleting-binary-file.yml
@@ -0,0 +1,5 @@
+---
+title: Removing an image should not output binary data
+merge_request: 30314
+author:
+type: fixed
diff --git a/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml b/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml
new file mode 100644
index 00000000000..3ee512f3448
--- /dev/null
+++ b/changelogs/unreleased/61005-grafanaInAdminSettingsMonitoringMenu.yml
@@ -0,0 +1,5 @@
+---
+title: Adds link to Grafana in Admin > Monitoring settings when grafana is enabled in config
+merge_request: 28937
+author: Romain Maneschi
+type: added
diff --git a/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml b/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml
new file mode 100644
index 00000000000..3445057f7fb
--- /dev/null
+++ b/changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml
@@ -0,0 +1,5 @@
+---
+title: Improved readability of storage statistics in group / project admin area
+merge_request: 30406
+author:
+type: other
diff --git a/changelogs/unreleased/63475-fix-n-1.yml b/changelogs/unreleased/63475-fix-n-1.yml
new file mode 100644
index 00000000000..3ed825290fd
--- /dev/null
+++ b/changelogs/unreleased/63475-fix-n-1.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of MergeRequestsController#ci_environment_status endpoint
+merge_request: 30224
+author:
+type: performance
diff --git a/changelogs/unreleased/63873-process-start-time.yml b/changelogs/unreleased/63873-process-start-time.yml
new file mode 100644
index 00000000000..b11a66ca106
--- /dev/null
+++ b/changelogs/unreleased/63873-process-start-time.yml
@@ -0,0 +1,6 @@
+---
+title: Change ruby_process_start_time_seconds metric to unix timestamp instead of
+ seconds from boot.
+merge_request: 30195
+author:
+type: fixed
diff --git a/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml b/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml
new file mode 100644
index 00000000000..a0ef34f3700
--- /dev/null
+++ b/changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml
@@ -0,0 +1,5 @@
+---
+title: Update mixin-deep to 1.3.2
+merge_request: 30223
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/64176-fix-error-handling.yml b/changelogs/unreleased/64176-fix-error-handling.yml
new file mode 100644
index 00000000000..e7a9a5897ae
--- /dev/null
+++ b/changelogs/unreleased/64176-fix-error-handling.yml
@@ -0,0 +1,5 @@
+---
+title: Fix invalid SSL certificate errors on Drone CI service
+merge_request: 30422
+author:
+type: fixed
diff --git a/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml b/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml
new file mode 100644
index 00000000000..3695f3063f3
--- /dev/null
+++ b/changelogs/unreleased/Remove-unresolved-class-in-discussion-header.yml
@@ -0,0 +1,5 @@
+---
+title: Remove unresolved class and fixed height in discussion header
+merge_request: 28440
+author: David Palubin
+type: other
diff --git a/changelogs/unreleased/add-salesforce-logo.yml b/changelogs/unreleased/add-salesforce-logo.yml
new file mode 100644
index 00000000000..13766821b88
--- /dev/null
+++ b/changelogs/unreleased/add-salesforce-logo.yml
@@ -0,0 +1,5 @@
+---
+title: Add salesforce logo for salesforce SSO
+merge_request: 28857
+author:
+type: changed
diff --git a/changelogs/unreleased/allow-reactive-caching-of-nil.yml b/changelogs/unreleased/allow-reactive-caching-of-nil.yml
new file mode 100644
index 00000000000..abd88c09d74
--- /dev/null
+++ b/changelogs/unreleased/allow-reactive-caching-of-nil.yml
@@ -0,0 +1,5 @@
+---
+title: Allow ReactiveCaching to support nil value
+merge_request: 30456
+author:
+type: performance
diff --git a/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml b/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml
new file mode 100644
index 00000000000..0500978a2e1
--- /dev/null
+++ b/changelogs/unreleased/api-doc-negative-commit-message-push-rule.yml
@@ -0,0 +1,5 @@
+---
+title: "Document the negative commit message push rule for the API."
+merge_request: 14004
+author: Maikel Vlasman
+type: added
diff --git a/changelogs/unreleased/centralize-markdownlint-config.yml b/changelogs/unreleased/centralize-markdownlint-config.yml
new file mode 100644
index 00000000000..9ca36078cf1
--- /dev/null
+++ b/changelogs/unreleased/centralize-markdownlint-config.yml
@@ -0,0 +1,5 @@
+---
+title: Centralize markdownlint configuration
+merge_request: 30263
+author:
+type: other
diff --git a/changelogs/unreleased/clusters-group-cte.yml b/changelogs/unreleased/clusters-group-cte.yml
new file mode 100644
index 00000000000..4b51249062d
--- /dev/null
+++ b/changelogs/unreleased/clusters-group-cte.yml
@@ -0,0 +1,5 @@
+---
+title: Use CTE to fetch clusters hierarchy in single query
+merge_request: 30063
+author:
+type: performance
diff --git a/changelogs/unreleased/create-merge-train-ref-ce.yml b/changelogs/unreleased/create-merge-train-ref-ce.yml
new file mode 100644
index 00000000000..b0b95275f58
--- /dev/null
+++ b/changelogs/unreleased/create-merge-train-ref-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Extend `MergeToRefService` to create merge ref from an arbitrary ref
+merge_request: 30361
+author:
+type: added
diff --git a/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml b/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml
new file mode 100644
index 00000000000..6ae6db08ba1
--- /dev/null
+++ b/changelogs/unreleased/fix-median-counting-for-cycle-analytics.yml
@@ -0,0 +1,5 @@
+---
+title: Fix median counting for cycle analytics
+merge_request: 30229
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml b/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml
new file mode 100644
index 00000000000..89ae4abfe11
--- /dev/null
+++ b/changelogs/unreleased/fix-sidekiq-transaction-check-race.yml
@@ -0,0 +1,5 @@
+---
+title: Fix race in forbid_sidekiq_in_transactions.rb
+merge_request: 30359
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-fix-subgroup-search-url.yml b/changelogs/unreleased/fj-fix-subgroup-search-url.yml
new file mode 100644
index 00000000000..bee6e97fb6f
--- /dev/null
+++ b/changelogs/unreleased/fj-fix-subgroup-search-url.yml
@@ -0,0 +1,5 @@
+---
+title: Fix subgroup url in search drop down
+merge_request: 30457
+author:
+type: fixed
diff --git a/changelogs/unreleased/gitaly-version-v1.51.0.yml b/changelogs/unreleased/gitaly-version-v1.51.0.yml
new file mode 100644
index 00000000000..00d52a190f3
--- /dev/null
+++ b/changelogs/unreleased/gitaly-version-v1.51.0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Gitaly v1.51.0
+merge_request: 30353
+author:
+type: changed
diff --git a/changelogs/unreleased/issue-63222.yml b/changelogs/unreleased/issue-63222.yml
new file mode 100644
index 00000000000..bc6520b9fc5
--- /dev/null
+++ b/changelogs/unreleased/issue-63222.yml
@@ -0,0 +1,5 @@
+---
+title: Set default sort method for dashboard projects list
+merge_request: 29830
+author: David Palubin
+type: fixed
diff --git a/changelogs/unreleased/issue_64021.yml b/changelogs/unreleased/issue_64021.yml
new file mode 100644
index 00000000000..088d9348619
--- /dev/null
+++ b/changelogs/unreleased/issue_64021.yml
@@ -0,0 +1,5 @@
+---
+title: Skip spam check for task list updates
+merge_request: 30279
+author:
+type: fixed
diff --git a/changelogs/unreleased/jc-detect-nfs-for-rugged.yml b/changelogs/unreleased/jc-detect-nfs-for-rugged.yml
new file mode 100644
index 00000000000..ca738181a55
--- /dev/null
+++ b/changelogs/unreleased/jc-detect-nfs-for-rugged.yml
@@ -0,0 +1,5 @@
+---
+title: Use Rugged if we detect storage is NFS and we can access the disk
+merge_request: 29725
+author:
+type: performance
diff --git a/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml b/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml
new file mode 100644
index 00000000000..b953d7c0fc8
--- /dev/null
+++ b/changelogs/unreleased/jramsay-enable-object-dedupe-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Enable Git object pools
+merge_request: 29595
+author: jramsay
+type: changed
diff --git a/changelogs/unreleased/limit-amount-of-tests-returned.yml b/changelogs/unreleased/limit-amount-of-tests-returned.yml
new file mode 100644
index 00000000000..0e80a64b6b7
--- /dev/null
+++ b/changelogs/unreleased/limit-amount-of-tests-returned.yml
@@ -0,0 +1,5 @@
+---
+title: Limit amount of JUnit tests returned
+merge_request: 30274
+author:
+type: performance
diff --git a/changelogs/unreleased/patch-29.yml b/changelogs/unreleased/patch-29.yml
new file mode 100644
index 00000000000..674c06e1259
--- /dev/null
+++ b/changelogs/unreleased/patch-29.yml
@@ -0,0 +1,5 @@
+---
+title: Updates PHP template to php:latest to ensure always targeting latest stable
+merge_request: 30319
+author: Paul Giberson
+type: changed
diff --git a/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml b/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml
new file mode 100644
index 00000000000..125b6244d80
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-flipper-checks-in-memory.yml
@@ -0,0 +1,5 @@
+---
+title: Cache Flipper feature flags in L1 and L2 caches
+merge_request: 30276
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml b/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml
new file mode 100644
index 00000000000..a0db68adb78
--- /dev/null
+++ b/changelogs/unreleased/sh-disable-reactive-caching-automatic-retries.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent amplification of ReactiveCachingWorker jobs upon failures
+merge_request: 30432
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fix-issue-63349.yml b/changelogs/unreleased/sh-fix-issue-63349.yml
new file mode 100644
index 00000000000..0e51a6b7b20
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-63349.yml
@@ -0,0 +1,5 @@
+---
+title: Make Housekeeping button do a full garbage collection
+merge_request: 30289
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml b/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml
new file mode 100644
index 00000000000..b408019c736
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-rouge-3-5-1.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Rouge to 3.5.1
+merge_request: 30431
+author:
+type: changed
diff --git a/changelogs/unreleased/tz-update-mr-count-over-tabs.yml b/changelogs/unreleased/tz-update-mr-count-over-tabs.yml
new file mode 100644
index 00000000000..61a49dd8ecc
--- /dev/null
+++ b/changelogs/unreleased/tz-update-mr-count-over-tabs.yml
@@ -0,0 +1,6 @@
+---
+title: New API for User Counts, updates on success of an MR the count on top and in
+ other tabs
+merge_request: 29441
+author:
+type: added
diff --git a/changelogs/unreleased/update-todo-in-ui.yml b/changelogs/unreleased/update-todo-in-ui.yml
new file mode 100644
index 00000000000..dddcf0f3983
--- /dev/null
+++ b/changelogs/unreleased/update-todo-in-ui.yml
@@ -0,0 +1,5 @@
+---
+title: Changes "Todo" to "To Do" in the UI for clarity
+merge_request: 28844
+author:
+type: other
diff --git a/changelogs/unreleased/winh-notes-service-applySuggestion.yml b/changelogs/unreleased/winh-notes-service-applySuggestion.yml
new file mode 100644
index 00000000000..30e540237b6
--- /dev/null
+++ b/changelogs/unreleased/winh-notes-service-applySuggestion.yml
@@ -0,0 +1,5 @@
+---
+title: Remove applySuggestion from notes service
+merge_request: 30399
+author: Frank van Rest
+type: other
diff --git a/config/README.md b/config/README.md
index 2778d0d4f02..9226f71a374 100644
--- a/config/README.md
+++ b/config/README.md
@@ -39,7 +39,7 @@ If desired, the routing URL provided by these settings can be used with:
1. `host name` or IP for each Redis instance desired
2. TCP port number for each Redis instance desired
3. `database number` for each Redis instance desired
-
+
## Example URL attribute formats for GitLab Redis `.yml` configuration files
* Unix Socket, default Redis database (0)
* `url: unix:/path/to/redis.sock`
@@ -147,4 +147,3 @@ searched):
3. the configuration file pointed to by the
`GITLAB_REDIS_CONFIG_FILE` environment variable
4. the configuration file `resque.yml`
-
diff --git a/config/application.rb b/config/application.rb
index c5ef6a2c60d..edf8b3e87f9 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -6,6 +6,7 @@ Bundler.require(:default, Rails.env)
module Gitlab
class Application < Rails::Application
+ require_dependency Rails.root.join('lib/gitlab')
require_dependency Rails.root.join('lib/gitlab/redis/wrapper')
require_dependency Rails.root.join('lib/gitlab/redis/cache')
require_dependency Rails.root.join('lib/gitlab/redis/queues')
diff --git a/config/initializers/0_license.rb b/config/initializers/0_license.rb
new file mode 100644
index 00000000000..f750022dfdf
--- /dev/null
+++ b/config/initializers/0_license.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+Gitlab.ee do
+ begin
+ public_key_file = File.read(Rails.root.join(".license_encryption_key.pub"))
+ public_key = OpenSSL::PKey::RSA.new(public_key_file)
+ Gitlab::License.encryption_key = public_key
+ rescue
+ warn "WARNING: No valid license encryption key provided."
+ end
+
+ # Needed to run migration
+ if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.data_source_exists?('licenses')
+ message = LicenseHelper.license_message(signed_in: true, is_admin: true, in_html: false)
+ if ::License.block_changes? && message.present?
+ warn "WARNING: #{message}"
+ end
+ end
+end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index bf187e9a282..0b8a6607250 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -76,6 +76,7 @@ Gitlab.ee do
Settings['smartcard'] ||= Settingslogic.new({})
Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil?
Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil?
+ Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil?
end
Settings['omniauth'] ||= Settingslogic.new({})
diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb
index 05eb395028d..04c109aa844 100644
--- a/config/initializers/console_message.rb
+++ b/config/initializers/console_message.rb
@@ -2,9 +2,18 @@
if defined?(Rails::Console)
# note that this will not print out when using `spring`
justify = 15
- puts "-------------------------------------------------------------------------------------"
+
+ puts '-' * 80
puts " GitLab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab.revision})"
puts " GitLab Shell:".ljust(justify) + "#{Gitlab::VersionInfo.parse(Gitlab::Shell.new.version)}"
puts " #{Gitlab::Database.human_adapter_name}:".ljust(justify) + Gitlab::Database.version
- puts "-------------------------------------------------------------------------------------"
+
+ Gitlab.ee do
+ if Gitlab::Geo.enabled?
+ puts " Geo enabled:".ljust(justify) + 'yes'
+ puts " Geo server:".ljust(justify) + EE::GeoHelper.current_node_human_status
+ end
+ end
+
+ puts '-' * 80
end
diff --git a/config/initializers/elastic_client_setup.rb b/config/initializers/elastic_client_setup.rb
new file mode 100644
index 00000000000..2ecb7956007
--- /dev/null
+++ b/config/initializers/elastic_client_setup.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+# Be sure to restart your server when you modify this file.
+
+require 'gitlab/current_settings'
+
+Gitlab.ee do
+ Elasticsearch::Model::Response::Records.prepend GemExtensions::Elasticsearch::Model::Response::Records
+ Elasticsearch::Model::Adapter::Multiple::Records.prepend GemExtensions::Elasticsearch::Model::Adapter::Multiple::Records
+ Elasticsearch::Model::Indexing::InstanceMethods.prepend GemExtensions::Elasticsearch::Model::Indexing::InstanceMethods
+
+ module Elasticsearch
+ module Model
+ module Client
+ # This mutex is only used to synchronize *creation* of a new client, so
+ # all including classes can share the same client instance
+ CLIENT_MUTEX = Mutex.new
+
+ cattr_accessor :cached_client
+ cattr_accessor :cached_config
+
+ module ClassMethods
+ # Override the default ::Elasticsearch::Model::Client implementation to
+ # return a client configured from application settings. All including
+ # classes will use the same instance, which is refreshed automatically
+ # if the settings change.
+ #
+ # _client is present to match the arity of the overridden method, where
+ # it is also not used.
+ #
+ # @return [Elasticsearch::Transport::Client]
+ def client(_client = nil)
+ store = ::Elasticsearch::Model::Client
+
+ store::CLIENT_MUTEX.synchronize do
+ config = Gitlab::CurrentSettings.elasticsearch_config
+
+ if store.cached_client.nil? || config != store.cached_config
+ store.cached_client = ::Gitlab::Elastic::Client.build(config)
+ store.cached_config = config
+ end
+ end
+
+ store.cached_client
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/config/initializers/forbid_sidekiq_in_transactions.rb b/config/initializers/forbid_sidekiq_in_transactions.rb
index a69f1ba090e..bb190af60b5 100644
--- a/config/initializers/forbid_sidekiq_in_transactions.rb
+++ b/config/initializers/forbid_sidekiq_in_transactions.rb
@@ -2,15 +2,16 @@ module Sidekiq
module Worker
EnqueueFromTransactionError = Class.new(StandardError)
- mattr_accessor :skip_transaction_check
- self.skip_transaction_check = false
-
def self.skipping_transaction_check(&block)
- skip_transaction_check = self.skip_transaction_check
- self.skip_transaction_check = true
+ previous_skip_transaction_check = self.skip_transaction_check
+ Thread.current[:sidekiq_worker_skip_transaction_check] = true
yield
ensure
- self.skip_transaction_check = skip_transaction_check
+ Thread.current[:sidekiq_worker_skip_transaction_check] = previous_skip_transaction_check
+ end
+
+ def self.skip_transaction_check
+ Thread.current[:sidekiq_worker_skip_transaction_check]
end
module ClassMethods
diff --git a/config/initializers/geo.rb b/config/initializers/geo.rb
new file mode 100644
index 00000000000..4cc9fbf49b2
--- /dev/null
+++ b/config/initializers/geo.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+Gitlab.ee do
+ if File.exist?(Rails.root.join('config/database_geo.yml'))
+ Rails.application.configure do
+ config.geo_database = config_for(:database_geo)
+ end
+ end
+
+ begin
+ if Gitlab::Geo.connected? && Gitlab::Geo.primary?
+ Gitlab::Geo.current_node&.update_clone_url!
+ end
+ rescue => e
+ warn "WARNING: Unable to check/update clone_url_prefix for Geo: #{e}"
+ end
+end
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
index 959daa93f78..9f466dc39de 100644
--- a/config/initializers/health_check.rb
+++ b/config/initializers/health_check.rb
@@ -1,4 +1,10 @@
HealthCheck.setup do |config|
config.standard_checks = %w(database migrations cache)
config.full_checks = %w(database migrations cache)
+
+ Gitlab.ee do
+ config.add_custom_check('geo') do
+ Gitlab::Geo::HealthCheck.new.perform_checks
+ end
+ end
end
diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb
new file mode 100644
index 00000000000..029c0ff4277
--- /dev/null
+++ b/config/initializers/load_balancing.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# 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 Gitlab::Database::LoadBalancing.enable?
+ Gitlab::Database.disable_prepared_statements
+
+ Gitlab::Application.configure do |config|
+ config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware)
+ end
+
+ Gitlab::Database::LoadBalancing.configure_proxy
+
+ # This needs to be executed after fork of clustered processes
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ # Service discovery must be started after configuring the proxy, as service
+ # discovery depends on this.
+ Gitlab::Database::LoadBalancing.start_service_discovery
+ end
+
+ end
+ end
+end
diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb
index cb108416b10..d492b60705d 100644
--- a/config/initializers/peek.rb
+++ b/config/initializers/peek.rb
@@ -42,3 +42,6 @@ class PEEK_DB_CLIENT
end
PEEK_DB_VIEW.prepend ::Gitlab::PerformanceBar::PeekQueryTracker
+
+require 'peek/adapters/redis'
+Peek::Adapters::Redis.prepend ::Gitlab::PerformanceBar::RedisAdapterWhenPeekEnabled
diff --git a/config/initializers/rack_attack_logging.rb b/config/initializers/rack_attack_logging.rb
index 338e968cc6c..7eb34bd69e5 100644
--- a/config/initializers/rack_attack_logging.rb
+++ b/config/initializers/rack_attack_logging.rb
@@ -12,7 +12,7 @@ ActiveSupport::Notifications.subscribe('rack.attack') do |name, start, finish, r
fullpath: req.fullpath
}
- if req.env['rack.attack.matched'] != 'throttle_unauthenticated'
+ if %w(throttle_authenticated_api throttle_authenticated_web).include? req.env['rack.attack.matched']
user_id = req.env['rack.attack.match_discriminator']
user = User.find_by(id: user_id)
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 7b69cf11288..f9ef5d66bfa 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -85,6 +85,19 @@ Sidekiq.configure_server do |config|
ActiveRecord::Base.establish_connection(db_config)
Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}")
+ Gitlab.ee do
+ Gitlab::Mirror.configure_cron_job!
+
+ Gitlab::Geo.configure_cron_jobs!
+
+ if Gitlab::Geo.geo_database_configured?
+ Rails.configuration.geo_database['pool'] = Sidekiq.options[:concurrency]
+ Geo::TrackingBase.establish_connection(Rails.configuration.geo_database)
+
+ Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{Geo::TrackingBase.connection_pool.size} (Geo tracking database)")
+ end
+ end
+
# Avoid autoload issue such as 'Mail::Parsers::AddressStruct'
# https://github.com/mikel/mail/issues/912#issuecomment-214850355
Mail.eager_autoload!
diff --git a/config/initializers/sidekiq_cluster.rb b/config/initializers/sidekiq_cluster.rb
new file mode 100644
index 00000000000..baa7495aa29
--- /dev/null
+++ b/config/initializers/sidekiq_cluster.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+if ENV['ENABLE_SIDEKIQ_CLUSTER'] && Gitlab.ee?
+ Thread.new do
+ Thread.current.abort_on_exception = true
+
+ parent = Process.ppid
+
+ loop do
+ sleep(5)
+
+ # In cluster mode it's possible that the master process is SIGKILL'd. In
+ # this case the parent PID changes and we need to terminate ourselves.
+ if Process.ppid != parent
+ Process.kill(:TERM, Process.pid)
+ break
+ end
+ end
+ end
+end
diff --git a/config/no_todos_messages.yml b/config/no_todos_messages.yml
index da721a9b6e6..d2076f235fd 100644
--- a/config/no_todos_messages.yml
+++ b/config/no_todos_messages.yml
@@ -4,8 +4,8 @@
# If you come up with a fun one, please feel free to contribute it to GitLab!
# https://about.gitlab.com/contributing/
---
-- Good job! Looks like you don't have any todos left
-- Isn't an empty todo list beautiful?
+- Good job! Looks like you don't have anything left on your To-Do List
+- Isn't an empty To-Do List beautiful?
- Give yourself a pat on the back!
-- Nothing left to do, high five!
-- Henceforth you shall be known as "Todo Destroyer"
+- Nothing left to do. High five!
+- Henceforth, you shall be known as "To-Do Destroyer"
diff --git a/config/routes/api.rb b/config/routes/api.rb
index 3ba9176d943..c6c17b5708e 100644
--- a/config/routes/api.rb
+++ b/config/routes/api.rb
@@ -1,7 +1,5 @@
-constraints(::Constraints::FeatureConstrainer.new(:graphql, default_enabled: true)) do
- post '/api/graphql', to: 'graphql#execute'
- mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: '/api/graphql'
-end
+post '/api/graphql', to: 'graphql#execute'
+mount GraphiQL::Rails::Engine, at: '/-/graphql-explorer', graphql_path: '/api/graphql'
::API::API.logger Rails.logger
mount ::API::API => '/'
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 19b48845305..a81590e8b8e 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -322,7 +322,10 @@ module.exports = {
}),
new webpack.DefinePlugin({
+ // This one is used to define window.gon.ee and other things properly in tests:
'process.env.IS_GITLAB_EE': JSON.stringify(IS_EE),
+ // This one is used to check against "EE" properly in application code
+ IS_EE: IS_EE ? 'window.gon && window.gon.ee' : JSON.stringify(false),
}),
].filter(Boolean),
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 4dadf60ad24..083e95b8da7 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -1,21 +1,5 @@
# frozen_string_literal: true
-# All the files/directories that should be reviewed by the DB team.
-DB_FILES = [
- 'db/',
- 'app/models/project_authorization.rb',
- 'app/services/users/refresh_authorized_projects_service.rb',
- 'lib/gitlab/background_migration.rb',
- 'lib/gitlab/background_migration/',
- 'lib/gitlab/database.rb',
- 'lib/gitlab/database/',
- 'lib/gitlab/github_import.rb',
- 'lib/gitlab/github_import/',
- 'lib/gitlab/sql/',
- 'rubocop/cop/migration',
- 'ee/lib/gitlab/database/'
-].freeze
-
SCHEMA_NOT_UPDATED_MESSAGE = <<~MSG
**New %<migrations>s added but %<schema>s wasn't updated.**
@@ -24,20 +8,6 @@ updated too (unless the migration isn't changing the DB schema
and isn't the most recent one).
MSG
-def database_paths_requiring_review(files)
- to_review = []
-
- files.each do |file|
- review = DB_FILES.any? do |pattern|
- file.start_with?(pattern)
- end
-
- to_review << file if review
- end
-
- to_review
-end
-
non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty?
geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
@@ -52,7 +22,7 @@ if geo_migration_created && !geo_db_schema_updated
warn format(SCHEMA_NOT_UPDATED_MESSAGE, migrations: 'Geo migrations', schema: gitlab.html_link("ee/db/geo/schema.rb"))
end
-db_paths_to_review = database_paths_requiring_review(helper.all_changed_files)
+db_paths_to_review = helper.changes_by_category[:database]
unless db_paths_to_review.empty?
message 'This merge request adds or changes files that require a ' \
diff --git a/danger/only_documentation/Dangerfile b/danger/only_documentation/Dangerfile
index 8e4564f22b6..ff65f8713d2 100644
--- a/danger/only_documentation/Dangerfile
+++ b/danger/only_documentation/Dangerfile
@@ -1,7 +1,7 @@
# rubocop:disable Style/SignalException
# frozen_string_literal: true
-has_only_docs_changes = helper.all_changed_files.all? { |file| file.start_with?('doc/') }
+has_only_docs_changes = helper.all_changed_files.all? { |file| file.start_with?('doc/', '.gitlab/ci/docs.gitlab-ci.yml', '.mdlrc') }
is_docs_only_branch = gitlab.branch_for_head =~ /(^docs[\/-].*|.*-docs$)/
if is_docs_only_branch && !has_only_docs_changes
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 7a86fe2eb7c..78ceb74da65 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -146,11 +146,13 @@ class Gitlab::Seeder::CycleAnalytics
commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for #{issue.to_reference}", branch_name: branch_name)
issue.project.repository.commit(commit_sha)
- GitPushService.new(issue.project,
- @user,
- oldrev: issue.project.repository.commit("master").sha,
- newrev: commit_sha,
- ref: 'refs/heads/master').execute
+ Git::BranchPushService.new(
+ issue.project,
+ @user,
+ oldrev: issue.project.repository.commit("master").sha,
+ newrev: commit_sha,
+ ref: 'refs/heads/master'
+ ).execute
branch_name
end
diff --git a/db/migrate/20190617123615_add_grafana_to_settings.rb b/db/migrate/20190617123615_add_grafana_to_settings.rb
new file mode 100644
index 00000000000..f9c6f4d883e
--- /dev/null
+++ b/db/migrate/20190617123615_add_grafana_to_settings.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddGrafanaToSettings < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:application_settings, :grafana_enabled, :boolean,
+ default: false, allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :grafana_enabled)
+ end
+end
diff --git a/db/migrate/20190621022810_add_last_ci_minutes_usage_notification_level_to_namespaces.rb b/db/migrate/20190621022810_add_last_ci_minutes_usage_notification_level_to_namespaces.rb
new file mode 100644
index 00000000000..1611340284c
--- /dev/null
+++ b/db/migrate/20190621022810_add_last_ci_minutes_usage_notification_level_to_namespaces.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddLastCiMinutesUsageNotificationLevelToNamespaces < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def change
+ add_column :namespaces, :last_ci_minutes_usage_notification_level, :integer
+ end
+end
diff --git a/db/migrate/20190621151636_add_merge_request_rebase_jid.rb b/db/migrate/20190621151636_add_merge_request_rebase_jid.rb
new file mode 100644
index 00000000000..1fed5690ead
--- /dev/null
+++ b/db/migrate/20190621151636_add_merge_request_rebase_jid.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddMergeRequestRebaseJid < ActiveRecord::Migration[5.1]
+ DOWNTIME = false
+
+ def change
+ add_column :merge_requests, :rebase_jid, :string
+ end
+end
diff --git a/db/migrate/20190624123615_add_grafana_url_to_settings.rb b/db/migrate/20190624123615_add_grafana_url_to_settings.rb
new file mode 100644
index 00000000000..61efe64a7a1
--- /dev/null
+++ b/db/migrate/20190624123615_add_grafana_url_to_settings.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddGrafanaUrlToSettings < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:application_settings, :grafana_url, :string,
+ default: '/-/grafana', allow_null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :grafana_url)
+ end
+end
diff --git a/db/migrate/20190703130053_remove_gitaly_feature_flags.rb b/db/migrate/20190703130053_remove_gitaly_feature_flags.rb
new file mode 100644
index 00000000000..13ac10a5e21
--- /dev/null
+++ b/db/migrate/20190703130053_remove_gitaly_feature_flags.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class RemoveGitalyFeatureFlags < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ FEATURES = %w[
+ gitaly_batch_lfs_pointers gitaly_blame gitaly_blob_get_all_lfs_pointers gitaly_blob_get_new_lfs_pointers
+ gitaly_branch_names gitaly_branch_names_contains_sha gitaly_branches gitaly_bundle_to_disk
+ gitaly_calculate_checksum gitaly_can_be_merged gitaly_cherry_pick gitaly_commit_count
+ gitaly_commit_deltas gitaly_commit_languages gitaly_commit_messages gitaly_commit_patch
+ gitaly_commit_raw_diffs gitaly_commit_stats gitaly_commit_tree_entry gitaly_commits_between
+ gitaly_commits_by_message gitaly_conflicts_list_conflict_files gitaly_conflicts_resolve_conflicts gitaly_count_commits
+ gitaly_count_diverging_commits_no_max gitaly_create_branch gitaly_create_repo_from_bundle gitaly_create_repository
+ gitaly_delete_branch gitaly_delete_refs gitaly_delta_islands gitaly_deny_disk_acces
+ gitaly_diff_between gitaly_extract_commit_signature gitaly_fetch_ref gitaly_fetch_remote
+ gitaly_fetch_source_branch gitaly_filter_shas_with_signature gitaly_filter_shas_with_signatures gitaly_find_all_commits
+ gitaly_find_branch gitaly_find_commit gitaly_find_commits gitaly_find_ref_name
+ gitaly_force_push gitaly_fork_repository gitaly_garbage_collect gitaly_get_info_attributes
+ gitaly_git_blob_load_all_data gitaly_git_blob_raw gitaly_git_fsck gitaly_go-find-all-tags
+ gitaly_has_local_branches gitaly_import_repository gitaly_is_ancestor gitaly_last_commit_for_path
+ gitaly_license_short_name gitaly_list_blobs_by_sha_path gitaly_list_commits_by_oid gitaly_local_branches
+ gitaly_ls_files gitaly_merge_base gitaly_merged_branch_names gitaly_new_commits
+ gitaly_operation_user_add_tag gitaly_operation_user_commit_file gitaly_operation_user_commit_files gitaly_operation_user_create_branch
+ gitaly_operation_user_delete_branch gitaly_operation_user_delete_tag gitaly_operation_user_ff_branch gitaly_operation_user_merge_branch
+ gitaly_post_receive_pack gitaly_post_upload_pack gitaly_project_raw_show gitaly_raw_changes_between
+ gitaly_rebase gitaly_rebase_in_progress gitaly_ref_delete_refs gitaly_ref_exists
+ gitaly_ref_exists_branch gitaly_ref_exists_branches gitaly_ref_find_all_remote_branches gitaly_remote_add_remote
+ gitaly_remote_fetch_internal_remote gitaly_remote_remove_remote gitaly_remote_update_remote_mirror gitaly_remove_namespace
+ gitaly_repack_full gitaly_repack_incremental gitaly_repository_cleanup gitaly_repository_exists
+ gitaly_repository_size gitaly_root_ref gitaly_search_files_by_content gitaly_search_files_by_name
+ gitaly_squash gitaly_squash_in_progress gitaly_ssh_receive_pack gitaly_ssh_upload_pack
+ gitaly_submodule_url_for gitaly_tag_messages gitaly_tag_names gitaly_tag_names_contains_sha
+ gitaly_tags gitaly_tree_entries gitaly_wiki_delete_page gitaly_wiki_find_file
+ gitaly_wiki_find_page gitaly_wiki_get_all_pages gitaly_wiki_page_formatted_data gitaly_wiki_page_versions
+ gitaly_wiki_update_page gitaly_wiki_write_page gitaly_workhorse_archive gitaly_workhorse_raw_show
+ gitaly_workhorse_send_git_diff gitaly_workhorse_send_git_patch gitaly_write_config gitaly_write_ref
+ ]
+
+ class Feature < ActiveRecord::Base
+ self.table_name = 'features'
+ end
+
+ def up
+ Feature.where(key: FEATURES).delete_all
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 32a25f643ce..9a8b64689bd 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: 20190628185004) do
+ActiveRecord::Schema.define(version: 20190703130053) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -193,6 +193,7 @@ ActiveRecord::Schema.define(version: 20190628185004) do
t.string "required_instance_ci_template"
t.boolean "dns_rebinding_protection_enabled", default: true, null: false
t.boolean "default_project_deletion_protection", default: false, null: false
+ t.boolean "grafana_enabled", default: false, null: false
t.boolean "lock_memberships_to_ldap", default: false, null: false
t.text "help_text"
t.boolean "elasticsearch_indexing", default: false, null: false
@@ -226,6 +227,7 @@ ActiveRecord::Schema.define(version: 20190628185004) do
t.boolean "elasticsearch_limit_indexing", default: false, null: false
t.string "geo_node_allowed_ips", default: "0.0.0.0/0, ::/0"
t.boolean "time_tracking_limit_to_hours", default: false, null: false
+ t.string "grafana_url", default: "/-/grafana", null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id", using: :btree
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id", using: :btree
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
@@ -1987,6 +1989,7 @@ ActiveRecord::Schema.define(version: 20190628185004) do
t.boolean "allow_maintainer_to_push"
t.integer "state_id", limit: 2
t.integer "approvals_before_merge"
+ t.string "rebase_jid"
t.index ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
t.index ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
t.index ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
@@ -2113,6 +2116,7 @@ ActiveRecord::Schema.define(version: 20190628185004) do
t.integer "extra_shared_runners_minutes_limit"
t.string "ldap_sync_status", default: "ready", null: false
t.boolean "membership_lock", default: false
+ t.integer "last_ci_minutes_usage_notification_level"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)", using: :btree
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id", using: :btree
diff --git a/doc/README.md b/doc/README.md
index 489c8117b9d..25db0efb960 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -86,7 +86,7 @@ The following documentation relates to the DevOps **Manage** stage:
| Manage Topics | Description |
|:--------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [Authentication and<br/>Authorization](administration/auth/README.md) **[CORE ONLY]** | Supported authentication and authorization providers. |
+| [Authentication and<br/>Authorization](administration/auth/README.md) **(CORE ONLY)** | Supported authentication and authorization providers. |
| [GitLab Cycle Analytics](user/project/cycle_analytics.md) | Measure the time it takes to go from an [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab) for each project you have. |
| [Instance Statistics](user/instance_statistics/index.md) | Discover statistics on how many GitLab features you use and user activity. |
@@ -107,18 +107,18 @@ The following documentation relates to the DevOps **Plan** stage:
| Plan Topics | Description |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
-| [Burndown Charts](user/project/milestones/burndown_charts.md) **[STARTER]** | Watch your project's progress throughout a specific milestone. |
+| [Burndown Charts](user/project/milestones/burndown_charts.md) **(STARTER)** | Watch your project's progress throughout a specific milestone. |
| [Discussions](user/discussions/index.md) | Threads, comments, and resolvable discussions in issues, commits, and merge requests. |
| [Due Dates](user/project/issues/due_dates.md) | Keep track of issue deadlines. |
-| [Epics](user/group/epics/index.md) **[ULTIMATE]** | Tracking groups of issues that share a theme. |
-| [Issues](user/project/issues/index.md), including [confidential issues](user/project/issues/confidential_issues.md),<br/>[issue and merge request templates](user/project/description_templates.md),<br/>and [moving issues](user/project/issues/moving_issues.md) | Project issues, restricting access to issues, create templates for submitting new issues and merge requests, and moving issues between projects. |
+| [Epics](user/group/epics/index.md) **(ULTIMATE)** | Tracking groups of issues that share a theme. |
+| [Issues](user/project/issues/index.md), including [confidential issues](user/project/issues/confidential_issues.md),<br/>[issue and merge request templates](user/project/description_templates.md),<br/>and [moving issues](user/project/issues/managing_issues.md#moving-issues) | Project issues, restricting access to issues, create templates for submitting new issues and merge requests, and moving issues between projects. |
| [Labels](user/project/labels.md) | Categorize issues or merge requests with descriptive labels. |
| [Milestones](user/project/milestones/index.md) | Set milestones for delivery of issues and merge requests, with optional due date. |
| [Project Issue Board](user/project/issue_board.md) | Display issues on a Scrum or Kanban board. |
| [Quick Actions](user/project/quick_actions.md) | Shortcuts for common actions on issues or merge requests, replacing the need to click buttons or use dropdowns in GitLab's UI. |
-| [Related Issues](user/project/issues/related_issues.md) **[STARTER]** | Create a relationship between issues. |
-| [Roadmap](user/group/roadmap/index.md) **[ULTIMATE]** | Visualize epic timelines. |
-| [Service Desk](user/project/service_desk.md) **[PREMIUM]** | A simple way to allow people to create issues in your GitLab instance without needing their own user account. |
+| [Related Issues](user/project/issues/related_issues.md) **(STARTER)** | Create a relationship between issues. |
+| [Roadmap](user/group/roadmap/index.md) **(ULTIMATE)** | Visualize epic timelines. |
+| [Service Desk](user/project/service_desk.md) **(PREMIUM)** | A simple way to allow people to create issues in your GitLab instance without needing their own user account. |
| [Time Tracking](workflow/time_tracking.md) | Track time spent on issues and merge requests. |
| [Todos](workflow/todos.md) | Keep track of work requiring attention with a chronological list displayed on a simple dashboard. |
@@ -143,14 +143,14 @@ The following documentation relates to the DevOps **Create** stage:
| Create Topics - Projects and Groups | Description |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------|
-| [Advanced global search](user/search/advanced_global_search.md) **[STARTER]** | Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. |
-| [Advanced syntax search](user/search/advanced_search_syntax.md) **[STARTER]** | Use advanced queries for more targeted search results. |
-| [Contribution analytics](user/group/contribution_analytics/index.md) **[STARTER]** | See detailed statistics of group contributors. |
+| [Advanced global search](user/search/advanced_global_search.md) **(STARTER)** | Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. |
+| [Advanced syntax search](user/search/advanced_search_syntax.md) **(STARTER)** | Use advanced queries for more targeted search results. |
+| [Contribution analytics](user/group/contribution_analytics/index.md) **(STARTER)** | See detailed statistics of group contributors. |
| [Create](gitlab-basics/create-project.md) and [fork](gitlab-basics/fork-project.md) projects, and<br/>[import and export projects<br/>between instances](user/project/settings/import_export.md) | Create, duplicate, and move projects. |
-| [File locking](user/project/file_lock.md) **[PREMIUM]** | Lock files to avoid merge conflicts. |
+| [File locking](user/project/file_lock.md) **(PREMIUM)** | Lock files to avoid merge conflicts. |
| [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy your static website with GitLab Pages. |
| [Groups](user/group/index.md) and [Subgroups](user/group/subgroups/index.md) | Organize your projects in groups. |
-| [Issues Analytics](user/group/issues_analytics/index.md) **[PREMIUM]** | Check how many issues were created per month. |
+| [Issues Analytics](user/group/issues_analytics/index.md) **(PREMIUM)** | Check how many issues were created per month. |
| [Projects](user/project/index.md), including [project access](public_access/public_access.md)<br/>and [settings](user/project/settings/index.md) | Host source code, and control your project's visibility and set configuration. |
| [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and todos. |
| [Snippets](user/snippets.md) | Snippets allow you to create little bits of code. |
@@ -175,9 +175,9 @@ The following documentation relates to the DevOps **Create** stage:
| [Files](user/project/repository/index.md#files) | Files management. |
| [Jupyter Notebook files](user/project/repository/index.md#jupyter-notebook-files) | GitLab's support for `.ipynb` files. |
| [Protected branches](user/project/protected_branches.md) | Use protected branches. |
-| [Push rules](push_rules/push_rules.md) **[STARTER]** | Additional control over pushes to your projects. |
+| [Push rules](push_rules/push_rules.md) **(STARTER)** | Additional control over pushes to your projects. |
| [Repositories](user/project/repository/index.md) | Manage source code repositories in GitLab's user interface. |
-| [Repository mirroring](workflow/repository_mirroring.md) **[STARTER]** | Push to or pull from repositories outside of GitLab |
+| [Repository mirroring](workflow/repository_mirroring.md) **(STARTER)** | Push to or pull from repositories outside of GitLab |
| [Start a merge request](user/project/repository/web_editor.md#tips) | Start merge request when committing via GitLab's user interface. |
<div align="right">
@@ -209,7 +209,7 @@ The following documentation relates to the DevOps **Create** stage:
| [GitLab API](api/README.md) | Integrate GitLab via a simple and powerful API. |
| [GitLab Integration](integration/README.md) | Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication. |
| [GitLab Webhooks](user/project/integrations/webhooks.md) | Let GitLab notify you when new code has been pushed to your project. |
-| [Jira Development Panel](integration/jira_development_panel.md) **[PREMIUM]** | See GitLab information in the Jira Development Panel. |
+| [Jira Development Panel](integration/jira_development_panel.md) **(PREMIUM)** | See GitLab information in the Jira Development Panel. |
| [Project Services](user/project/integrations/project_services.md) | Integrate a project with external services, such as CI and chat. |
| [Trello Power-Up](integration/trello_power_up.md) | Integrate with GitLab's Trello Power-Up. |
@@ -233,10 +233,10 @@ The following documentation relates to the DevOps **Verify** stage:
| Verify Topics | Description |
|:----------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------|
-| [Code Quality reports](user/project/merge_requests/code_quality.md) **[STARTER]** | Analyze source code quality. |
+| [Code Quality reports](user/project/merge_requests/code_quality.md) **(STARTER)** | Analyze source code quality. |
| [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Integration with GitLab. |
| [JUnit test reports](ci/junit_test_reports.md) | Display JUnit test reports on merge requests. |
-| [Multi-project pipelines](ci/multi_project_pipelines.md) **[PREMIUM]** | Visualize entire pipelines that span multiple projects, including all cross-project inter-dependencies. |
+| [Multi-project pipelines](ci/multi_project_pipelines.md) **(PREMIUM)** | Visualize entire pipelines that span multiple projects, including all cross-project inter-dependencies. |
| [Pipeline Graphs](ci/pipelines.md#visualizing-pipelines) | Visualize builds. |
| [Review Apps](ci/review_apps/index.md) | Preview changes to your application right from a merge request. |
@@ -257,7 +257,7 @@ The following documentation relates to the DevOps **Package** stage:
| Package Topics | Description |
|:----------------------------------------------------------------|:-------------------------------------------------------|
| [GitLab Container Registry](user/project/container_registry.md) | Learn how to use GitLab's built-in Container Registry. |
-| [GitLab Packages](administration/packages.md) **[PREMIUM]** | Use GitLab as an NPM registry or Maven repository. |
+| [GitLab Packages](administration/packages.md) **(PREMIUM)** | Use GitLab as an NPM registry or Maven repository. |
<div align="right">
<a type="button" class="btn btn-default" href="#overview">
@@ -276,10 +276,10 @@ The following documentation relates to the DevOps **Release** stage:
| Release Topics | Description |
|:------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
| [Auto Deploy](topics/autodevops/index.md#auto-deploy) | Configure GitLab for the deployment of your application. |
-| [Canary Deployments](user/project/canary_deployments.md) **[PREMIUM]** | Employ a popular CI strategy where a small portion of the fleet is updated to the new version first. |
-| [Deploy Boards](user/project/deploy_boards.md) **[PREMIUM]** | View the current health and status of each CI environment running on Kubernetes, displaying the status of the pods in the deployment. |
+| [Canary Deployments](user/project/canary_deployments.md) **(PREMIUM)** | Employ a popular CI strategy where a small portion of the fleet is updated to the new version first. |
+| [Deploy Boards](user/project/deploy_boards.md) **(PREMIUM)** | View the current health and status of each CI environment running on Kubernetes, displaying the status of the pods in the deployment. |
| [Environments and deployments](ci/environments.md) | With environments, you can control the continuous deployment of your software within GitLab. |
-| [Environment-specific variables](ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) **[PREMIUM]** | Limit scope of variables to specific environments. |
+| [Environment-specific variables](ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium) **(PREMIUM)** | Limit scope of variables to specific environments. |
| [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Deployment and Delivery with GitLab. |
| [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy a static site directly from GitLab. |
| [Protected Runners](ci/runners/README.md#protected-runners) | Select Runners to only pick jobs for protected branches and tags. |
@@ -307,7 +307,7 @@ The following documentation relates to the DevOps **Configure** stage:
| [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. |
| [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. |
-| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **[PREMIUM]** | Associate more than one Kubernetes clusters to your project. |
+| [Multiple Kubernetes Clusters](user/project/clusters/index.md#multiple-kubernetes-clusters-premium) **(PREMIUM)** | Associate more than one Kubernetes clusters to your project. |
| [Protected variables](ci/variables/README.md#protected-environment-variables) | Restrict variables to protected branches and tags. |
| [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. |
| [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. |
@@ -329,8 +329,8 @@ The following documentation relates to the DevOps **Monitor** stage:
| Monitor Topics | Description |
|:------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
-| [GitLab Performance Monitoring](administration/monitoring/performance/index.md) **[CORE ONLY]** | Use InfluxDB and Grafana to monitor the performance of your GitLab instance (will be eventually replaced by Prometheus). |
-| [GitLab Prometheus](administration/monitoring/prometheus/index.md) **[CORE ONLY]** | Configure the bundled Prometheus to collect various metrics from your GitLab instance. |
+| [GitLab Performance Monitoring](administration/monitoring/performance/index.md) **(CORE ONLY)** | Use InfluxDB and Grafana to monitor the performance of your GitLab instance (will be eventually replaced by Prometheus). |
+| [GitLab Prometheus](administration/monitoring/prometheus/index.md) **(CORE ONLY)** | Configure the bundled Prometheus to collect various metrics from your GitLab instance. |
| [Health check](user/admin_area/monitoring/health_check.md) | GitLab provides liveness and readiness probes to indicate service health and reachability to required services. |
| [Prometheus project integration](user/project/integrations/prometheus.md) | Configure the Prometheus integration per project and monitor your CI/CD environments. |
| [Prometheus metrics](user/project/integrations/prometheus_library/index.md) | Let Prometheus collect metrics from various services, like Kubernetes, NGINX, NGINX ingress controller, HAProxy, and Amazon Cloud Watch. |
@@ -353,13 +353,13 @@ The following documentation relates to the DevOps **Secure** stage:
| Secure Topics | Description |
|:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
-| [Container Scanning](user/application_security/container_scanning/index.md) **[ULTIMATE]** | Use Clair to scan docker images for known vulnerabilities. |
-| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. |
-| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **[ULTIMATE]** | Analyze running web applications for known vulnerabilities. |
-| [Group Security Dashboard](user/application_security/security_dashboard/index.md) **[ULTIMATE]** | View vulnerabilities in all the projects in a group and its subgroups. |
-| [License Management](user/application_security/license_management/index.md) **[ULTIMATE]** | Search your project's dependencies for their licenses. |
-| [Project Security Dashboard](user/application_security/security_dashboard/index.md) **[ULTIMATE]** | View the latest security reports for your project. |
-| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **[ULTIMATE]** | Analyze source code for known vulnerabilities. |
+| [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan docker images for known vulnerabilities. |
+| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
+| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
+| [Group Security Dashboard](user/application_security/security_dashboard/index.md) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
+| [License Management](user/application_security/license_management/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
+| [Project Security Dashboard](user/application_security/security_dashboard/index.md) **(ULTIMATE)** | View the latest security reports for your project. |
+| [Static Application Security Testing (SAST)](user/application_security/sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. |
## Subscribe to GitLab
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index d7a2e13b53e..a80ff330e03 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -2,7 +2,7 @@
last_updated: 2019-02-04
---
-# Audit Events **[STARTER]**
+# Audit Events **(STARTER)**
GitLab offers a way to view the changes made within the GitLab server for owners and administrators on a [paid plan][ee].
@@ -32,7 +32,7 @@ There are two kinds of events logged:
- Instance events scoped to the whole GitLab instance, used by your Compliance team to
perform formal audits.
-### Group events **[STARTER]**
+### Group events **(STARTER)**
NOTE: **Note:**
You need Owner [permissions] to view the group Audit Events page.
@@ -59,7 +59,7 @@ From there, you can see the following actions:
- 2FA enforcement/grace period changed
- Roles allowed to create project changed
-### Project events **[STARTER]**
+### Project events **(STARTER)**
NOTE: **Note:**
You need Maintainer [permissions] or higher to view the project Audit Events page.
@@ -74,7 +74,7 @@ From there, you can see the following actions:
- Permission changes of a user assigned to a project
- User was removed from project
-### Instance events **[PREMIUM ONLY]**
+### Instance events **(PREMIUM ONLY)**
> [Introduced][ee-2336] in [GitLab Premium][ee] 9.3.
@@ -99,7 +99,7 @@ It is possible to filter particular actions by choosing an audit data type from
the filter drop-down. You can further filter by specific group, project or user
(for authentication events).
-![audit log](audit_log.png)
+![audit log](img/audit_log.png)
### Missing events
diff --git a/doc/administration/auditor_users.md b/doc/administration/auditor_users.md
index ef8c8197d6d..65d36612d85 100644
--- a/doc/administration/auditor_users.md
+++ b/doc/administration/auditor_users.md
@@ -1,4 +1,4 @@
-# Auditor users **[PREMIUM ONLY]**
+# Auditor users **(PREMIUM ONLY)**
>[Introduced][ee-998] in [GitLab Premium][eep] 8.17.
@@ -52,7 +52,7 @@ section.
**Admin Area > Users**. You will find the option of the access level under
the 'Access' section.
- ![Admin Area Form](auditor_access_form.png)
+ ![Admin Area Form](img/auditor_access_form.png)
1. Click **Save changes** or **Create user** for the changes to take effect.
diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md
index e215a0df6ec..d8094587d14 100644
--- a/doc/administration/auth/README.md
+++ b/doc/administration/auth/README.md
@@ -9,11 +9,11 @@ providers.
- [LDAP](ldap.md) Includes Active Directory, Apple Open Directory, Open LDAP,
and 389 Server
- - [LDAP for GitLab EE](ldap-ee.md): LDAP additions to GitLab Enterprise Editions **[STARTER ONLY]**
+ - [LDAP for GitLab EE](ldap-ee.md): LDAP additions to GitLab Enterprise Editions **(STARTER ONLY)**
- [OmniAuth](../../integration/omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google,
Bitbucket, Facebook, Shibboleth, Crowd, Azure, Authentiq ID, and JWT
- [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS
- [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider
- [Okta](okta.md) Configure GitLab to sign in using Okta
- [Authentiq](authentiq.md): Enable the Authentiq OmniAuth provider for passwordless authentication
-- [Smartcard](smartcard.md) Smartcard authentication **[PREMIUM ONLY]**
+- [Smartcard](smartcard.md) Smartcard authentication **(PREMIUM ONLY)**
diff --git a/doc/administration/auth/google_secure_ldap.md b/doc/administration/auth/google_secure_ldap.md
index c668f19ca7d..1db5bb4bc3f 100644
--- a/doc/administration/auth/google_secure_ldap.md
+++ b/doc/administration/auth/google_secure_ldap.md
@@ -1,4 +1,4 @@
-# Google Secure LDAP **[CORE ONLY]**
+# Google Secure LDAP **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/46391) in GitLab 11.9.
@@ -13,7 +13,7 @@ The steps below cover:
## Configuring Google LDAP client
-1. Navigate to <https://admin.google.com> and sign in as a GSuite domain administrator.
+1. Navigate to <https://admin.google.com/Dashboard> and sign in as a GSuite domain administrator.
1. Go to **Apps > LDAP > Add Client**.
@@ -202,6 +202,5 @@ values obtained during the LDAP client configuration earlier:
1. Save the file and [restart] GitLab for the changes to take effect.
-
[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: ../restart_gitlab.md#installations-from-source
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index 1f67e8f5744..320a65b665d 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -111,7 +111,7 @@ The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb`
The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix)
-> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features) **[STARTER ONLY]**
+> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](../how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features) **(STARTER ONLY)**
### Example `gitlab.rb` LDAP
@@ -267,4 +267,4 @@ have extended functionalities with LDAP, such as:
- Updating user permissions
- Multiple LDAP servers
-Read through the article on [LDAP for GitLab EE](../how_to_configure_ldap_gitlab_ee/index.md) **[STARTER ONLY]** for an overview.
+Read through the article on [LDAP for GitLab EE](../how_to_configure_ldap_gitlab_ee/index.md) **(STARTER ONLY)** for an overview.
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
index 4d82a7370bb..2683950f143 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ee/index.md
@@ -6,7 +6,7 @@ article_type: admin guide
date: 2017-05-03
---
-# How to configure LDAP with GitLab EE **[STARTER ONLY]**
+# How to configure LDAP with GitLab EE **(STARTER ONLY)**
## Introduction
diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md
index b45966fa920..1a8af0827ee 100644
--- a/doc/administration/auth/ldap-ee.md
+++ b/doc/administration/auth/ldap-ee.md
@@ -1,4 +1,4 @@
-# LDAP Additions in GitLab EE **[STARTER ONLY]**
+# LDAP Additions in GitLab EE **(STARTER ONLY)**
This is a continuation of the main [LDAP documentation](ldap.md), detailing LDAP
features specific to GitLab Enterprise Edition Starter, Premium and Ultimate.
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 79ac7fe0352..2144f5753a8 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -12,7 +12,7 @@ including group membership syncing as well as multiple LDAP servers support.
The information on this page is relevant for both GitLab CE and EE. For more
details about EE-specific LDAP features, see the
-[LDAP Enterprise Edition documentation](ldap-ee.md). **[STARTER ONLY]**
+[LDAP Enterprise Edition documentation](ldap-ee.md). **(STARTER ONLY)**
## Security
@@ -46,7 +46,7 @@ LDAP-enabled users can always authenticate with Git using their GitLab username
or email and LDAP password, even if password authentication for Git is disabled
in the application settings.
-## Google Secure LDAP **[CORE ONLY]**
+## Google Secure LDAP **(CORE ONLY)**
> Introduced in GitLab 11.9.
@@ -62,7 +62,7 @@ to connect to one GitLab server.
For a complete guide on configuring LDAP with GitLab Community Edition, please check
the admin guide [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md).
-For GitLab Enterprise Editions, see also [How to configure LDAP with GitLab EE](how_to_configure_ldap_gitlab_ee/index.md). **[STARTER ONLY]**
+For GitLab Enterprise Editions, see also [How to configure LDAP with GitLab EE](how_to_configure_ldap_gitlab_ee/index.md). **(STARTER ONLY)**
To enable LDAP integration you need to add your LDAP server settings in
`/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml` for Omnibus
@@ -387,7 +387,7 @@ group, you can use the following syntax:
Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
<https://docs.microsoft.com/en-us/windows/desktop/ADSI/search-filter-syntax>. Support for
nested members in the user filter should not be confused with
-[group sync nested groups support](ldap-ee.md#supported-ldap-group-typesattributes). **[STARTER ONLY]**
+[group sync nested groups support](ldap-ee.md#supported-ldap-group-typesattributes). **(STARTER ONLY)**
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md
index b33c5359b44..a0d4e9ef3b5 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -1,4 +1,4 @@
-# Smartcard authentication **[PREMIUM ONLY]**
+# Smartcard authentication **(PREMIUM ONLY)**
GitLab supports authentication using smartcards.
@@ -184,3 +184,31 @@ attribute. As a prerequisite, you must use an LDAP server that:
1. Save the file and [restart](../restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
+
+### Require browser session with smartcard sign-in for Git access
+
+**For Omnibus installations**
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['smartcard_required_for_git_access'] = true
+ ```
+
+1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure)
+ GitLab for the changes to take effect.
+
+**For installations from source**
+
+1. Edit `config/gitlab.yml`:
+
+ ```yaml
+ ## Smartcard authentication settings
+ smartcard:
+ # snip...
+ # Browser session with smartcard sign-in is required for Git access
+ required_for_git_access: true
+ ```
+
+1. Save the file and [restart](../restart_gitlab.md#installations-from-source)
+ GitLab for the changes to take effect.
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 2e4b4efa0ac..04f52783d22 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -1,7 +1,5 @@
# GitLab Container Registry administration
-> **Notes:**
->
> - [Introduced][ce-4040] in GitLab 8.8.
> - Container Registry manifest `v1` support was added in GitLab 8.9 to support
> Docker versions earlier than 1.10.
@@ -125,21 +123,21 @@ otherwise you will run into conflicts.
1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
path to the existing TLS certificate and key used by GitLab:
- ```ruby
- registry_external_url 'https://gitlab.example.com:4567'
- ```
+ ```ruby
+ registry_external_url 'https://gitlab.example.com:4567'
+ ```
- Note how the `registry_external_url` is listening on HTTPS under the
- existing GitLab URL, but on a different port.
+ Note how the `registry_external_url` is listening on HTTPS under the
+ existing GitLab URL, but on a different port.
- If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt`
- and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines
- below:
+ If your TLS certificate is not in `/etc/gitlab/ssl/gitlab.example.com.crt`
+ and key not in `/etc/gitlab/ssl/gitlab.example.com.key` uncomment the lines
+ below:
- ```ruby
- registry_nginx['ssl_certificate'] = "/path/to/certificate.pem"
- registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key"
- ```
+ ```ruby
+ registry_nginx['ssl_certificate'] = "/path/to/certificate.pem"
+ registry_nginx['ssl_certificate_key'] = "/path/to/certificate.key"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -150,12 +148,12 @@ otherwise you will run into conflicts.
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
configure it with the following settings:
- ```
- registry:
- enabled: true
- host: gitlab.example.com
- port: 4567
- ```
+ ```
+ registry:
+ enabled: true
+ host: gitlab.example.com
+ port: 4567
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
@@ -188,17 +186,17 @@ Let's assume that you want the container Registry to be accessible at
`/etc/gitlab/ssl/registry.gitlab.example.com.key` and make sure they have
correct permissions:
- ```bash
- chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.*
- ```
+ ```bash
+ chmod 600 /etc/gitlab/ssl/registry.gitlab.example.com.*
+ ```
1. Once the TLS certificate is in place, edit `/etc/gitlab/gitlab.rb` with:
- ```ruby
- registry_external_url 'https://registry.gitlab.example.com'
- ```
+ ```ruby
+ registry_external_url 'https://registry.gitlab.example.com'
+ ```
- Note how the `registry_external_url` is listening on HTTPS.
+ Note how the `registry_external_url` is listening on HTTPS.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -219,11 +217,11 @@ look like:
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
configure it with the following settings:
- ```
- registry:
- enabled: true
- host: registry.gitlab.example.com
- ```
+ ```yaml
+ registry:
+ enabled: true
+ host: registry.gitlab.example.com
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Make the relevant changes in NGINX as well (domain, port, TLS certificates path).
@@ -248,9 +246,9 @@ Registry application itself.
1. Open `/etc/gitlab/gitlab.rb` and set `registry['enable']` to `false`:
- ```ruby
- registry['enable'] = false
- ```
+ ```ruby
+ registry['enable'] = false
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -261,10 +259,10 @@ Registry application itself.
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
set `enabled` to `false`:
- ```
- registry:
- enabled: false
- ```
+ ```yaml
+ registry:
+ enabled: false
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -280,9 +278,9 @@ the Container Registry by themselves, follow the steps below.
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['gitlab_default_projects_features_container_registry'] = false
- ```
+ ```ruby
+ gitlab_rails['gitlab_default_projects_features_container_registry'] = false
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -293,16 +291,16 @@ the Container Registry by themselves, follow the steps below.
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features`
entry and configure it so that `container_registry` is set to `false`:
- ```
- ## Default project features settings
- default_projects_features:
- issues: true
- merge_requests: true
- wiki: true
- snippets: false
- builds: true
- container_registry: false
- ```
+ ```yaml
+ ## Default project features settings
+ default_projects_features:
+ issues: true
+ merge_requests: true
+ wiki: true
+ snippets: false
+ builds: true
+ container_registry: false
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -332,9 +330,9 @@ The default location where images are stored in Omnibus, is
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- gitlab_rails['registry_path'] = "/path/to/registry/storage"
- ```
+ ```ruby
+ gitlab_rails['registry_path'] = "/path/to/registry/storage"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -348,10 +346,10 @@ The default location where images are stored in source installations, is
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
change the `path` setting:
- ```
- registry:
- path: shared/registry
- ```
+ ```yaml
+ registry:
+ path: shared/registry
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -393,17 +391,17 @@ To configure the `s3` storage driver in Omnibus:
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- registry['storage'] = {
- 's3' => {
- 'accesskey' => 's3-access-key',
- 'secretkey' => 's3-secret-key-for-access-key',
- 'bucket' => 'your-s3-bucket',
- 'region' => 'your-s3-region',
- 'regionendpoint' => 'your-s3-regionendpoint'
- }
- }
- ```
+ ```ruby
+ registry['storage'] = {
+ 's3' => {
+ 'accesskey' => 's3-access-key',
+ 'secretkey' => 's3-secret-key-for-access-key',
+ 'bucket' => 'your-s3-bucket',
+ 'region' => 'your-s3-region',
+ 'regionendpoint' => 'your-s3-regionendpoint'
+ }
+ }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -442,9 +440,9 @@ In the examples below we set the Registry's port to `5001`.
1. Open `/etc/gitlab/gitlab.rb` and set `registry['registry_http_addr']`:
- ```ruby
- registry['registry_http_addr'] = "localhost:5001"
- ```
+ ```ruby
+ registry['registry_http_addr'] = "localhost:5001"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -455,10 +453,10 @@ In the examples below we set the Registry's port to `5001`.
1. Open the configuration file of your Registry server and edit the
[`http:addr`][registry-http-config] value:
- ```
- http
- addr: localhost:5001
- ```
+ ```yaml
+ http
+ addr: localhost:5001
+ ```
1. Save the file and restart the Registry server.
@@ -476,14 +474,14 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry.
1. Open `/etc/gitlab/gitlab.rb` and set necessary configurations:
- ```ruby
- gitlab_rails['registry_enabled'] = true
- gitlab_rails['registry_host'] = "registry.gitlab.example.com"
- gitlab_rails['registry_port'] = "5005"
- gitlab_rails['registry_api_url'] = "http://localhost:5000"
- gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
- gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
- ```
+ ```ruby
+ gitlab_rails['registry_enabled'] = true
+ gitlab_rails['registry_host'] = "registry.gitlab.example.com"
+ gitlab_rails['registry_port'] = "5005"
+ gitlab_rails['registry_api_url'] = "http://localhost:5000"
+ gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
+ gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
+ ```
1. A certificate keypair is required for GitLab and the Container Registry to
communicate securely. By default omnibus-gitlab will generate one keypair,
@@ -492,19 +490,19 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry.
custom certificate key. To do that, add the following to
`/etc/gitlab/gitlab.rb`
- ```ruby
- gitlab_rails['registry_key_path'] = "/custom/path/to/registry-key.key"
- # registry['internal_key'] should contain the contents of the custom key
- # file. Line breaks in the key file should be marked using `\n` character
- # Example:
- registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n"
- ```
+ ```ruby
+ gitlab_rails['registry_key_path'] = "/custom/path/to/registry-key.key"
+ # registry['internal_key'] should contain the contents of the custom key
+ # file. Line breaks in the key file should be marked using `\n` character
+ # Example:
+ registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n"
+ ```
- **Note:** The file specified at `registry_key_path` gets populated with the
- content specified by `internal_key`, each time reconfigure is executed. If
- no file is specified, omnibus-gitlab will default it to
- `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key` and will populate
- it.
+ **Note:** The file specified at `registry_key_path` gets populated with the
+ content specified by `internal_key`, each time reconfigure is executed. If
+ no file is specified, omnibus-gitlab will default it to
+ `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key` and will populate
+ it.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -512,18 +510,18 @@ You can use GitLab as an auth endpoint and use a non-bundled Container Registry.
1. Open `/home/git/gitlab/config/gitlab.yml`, and edit the configuration settings under `registry`:
- ```
- ## Container Registry
+ ```yaml
+ ## Container Registry
- registry:
- enabled: true
- host: "registry.gitlab.example.com"
- port: "5005"
- api_url: "http://localhost:5000"
- path: /var/opt/gitlab/gitlab-rails/shared/registry
- key: /var/opt/gitlab/gitlab-rails/certificate.key
- issuer: omnibus-gitlab-issuer
- ```
+ registry:
+ enabled: true
+ host: "registry.gitlab.example.com"
+ port: "5005"
+ api_url: "http://localhost:5000"
+ path: /var/opt/gitlab/gitlab-rails/shared/registry
+ key: /var/opt/gitlab/gitlab-rails/certificate.key
+ issuer: omnibus-gitlab-issuer
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -550,20 +548,20 @@ To configure a notification endpoint in Omnibus:
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- registry['notifications'] = [
- {
- 'name' => 'test_endpoint',
- 'url' => 'https://gitlab.example.com/notify',
- 'timeout' => '500ms',
- 'threshold' => 5,
- 'backoff' => '1s',
- 'headers' => {
- "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"]
- }
- }
- ]
- ```
+ ```ruby
+ registry['notifications'] = [
+ {
+ 'name' => 'test_endpoint',
+ 'url' => 'https://gitlab.example.com/notify',
+ 'timeout' => '500ms',
+ 'threshold' => 5,
+ 'backoff' => '1s',
+ 'headers' => {
+ "Authorization" => ["AUTHORIZATION_EXAMPLE_TOKEN"]
+ }
+ }
+ ]
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -629,16 +627,16 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB).
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- registry['storage'] = {
- 's3' => {
- 'accesskey' => 'AKIAKIAKI',
- 'secretkey' => 'secret123',
- 'bucket' => 'gitlab-registry-bucket-AKIAKIAKI',
- 'chunksize' => 25000000
- }
- }
- ```
+ ```ruby
+ registry['storage'] = {
+ 's3' => {
+ 'accesskey' => 'AKIAKIAKI',
+ 'secretkey' => 'secret123',
+ 'bucket' => 'gitlab-registry-bucket-AKIAKIAKI',
+ 'chunksize' => 25000000
+ }
+ }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -648,14 +646,14 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB).
1. Edit `config/gitlab.yml`:
- ```yaml
- storage:
- s3:
- accesskey: 'AKIAKIAKI'
- secretkey: 'secret123'
- bucket: 'gitlab-registry-bucket-AKIAKIAKI'
- chunksize: 25000000
- ```
+ ```yaml
+ storage:
+ s3:
+ accesskey: 'AKIAKIAKI'
+ secretkey: 'secret123'
+ bucket: 'gitlab-registry-bucket-AKIAKIAKI'
+ chunksize: 25000000
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -669,9 +667,9 @@ You can add a configuration option for backwards compatibility.
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- registry['compatibility_schema1_enabled'] = true
- ```
+ ```ruby
+ registry['compatibility_schema1_enabled'] = true
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -681,11 +679,11 @@ You can add a configuration option for backwards compatibility.
1. Edit the YML configuration file you created when you [deployed the registry][registry-deploy]. Add the following snippet:
- ```yaml
- compatibility:
- schema1:
- enabled: true
- ```
+ ```yaml
+ compatibility:
+ schema1:
+ enabled: true
+ ```
1. Restart the registry for the changes to take affect.
@@ -694,9 +692,9 @@ You can add a configuration option for backwards compatibility.
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](../user/group/index.md#changing-a-groups-path),
[change the project path](../user/project/settings/index.md#renaming-a-repository) or change the
diff --git a/doc/administration/database_load_balancing.md b/doc/administration/database_load_balancing.md
index 98404ff2a10..dc4cc401fca 100644
--- a/doc/administration/database_load_balancing.md
+++ b/doc/administration/database_load_balancing.md
@@ -1,4 +1,4 @@
-# Database Load Balancing **[PREMIUM ONLY]**
+# Database Load Balancing **(PREMIUM ONLY)**
> [Introduced][ee-1283] in [GitLab Premium][eep] 9.0.
@@ -74,9 +74,9 @@ the following. This will balance the load between `host1.example.com` and
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] }
- ```
+ ```ruby
+ gitlab_rails['db_load_balancing'] = { 'hosts' => ['host1.example.com', 'host2.example.com'] }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -86,16 +86,16 @@ the following. This will balance the load between `host1.example.com` and
1. Edit `/home/git/gitlab/config/database.yml` and add or amend the following lines:
- ```yaml
- production:
- username: gitlab
- database: gitlab
- encoding: unicode
- load_balancing:
- hosts:
- - host1.example.com
- - host2.example.com
- ```
+ ```yaml
+ production:
+ username: gitlab
+ database: gitlab
+ encoding: unicode
+ load_balancing:
+ hosts:
+ - host1.example.com
+ - host2.example.com
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -265,7 +265,7 @@ production:
replica_check_interval: 30
```
-[hot-standby]: https://www.postgresql.org/docs/9.6/static/hot-standby.html
+[hot-standby]: https://www.postgresql.org/docs/9.6/hot-standby.html
[ee-1283]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1283
[eep]: https://about.gitlab.com/pricing/
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
diff --git a/doc/administration/dependency_proxy.md b/doc/administration/dependency_proxy.md
index 4dc1f4dcba4..776c60703fc 100644
--- a/doc/administration/dependency_proxy.md
+++ b/doc/administration/dependency_proxy.md
@@ -1,6 +1,6 @@
-# GitLab Dependency Proxy administration **[PREMIUM ONLY]**
+# GitLab Dependency Proxy administration **(PREMIUM ONLY)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing) 11.11.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
GitLab can be utilized as a dependency proxy for a variety of common package managers.
diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index e19cd9bbfec..8eee9427b56 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -1,4 +1,4 @@
-# Automatic background verification **[PREMIUM ONLY]**
+# Automatic background verification **(PREMIUM ONLY)**
NOTE: **Note:**
Automatic background verification of repositories and wikis was added in
diff --git a/doc/administration/geo/disaster_recovery/bring_primary_back.md b/doc/administration/geo/disaster_recovery/bring_primary_back.md
index 9a981b49349..30d35df25c7 100644
--- a/doc/administration/geo/disaster_recovery/bring_primary_back.md
+++ b/doc/administration/geo/disaster_recovery/bring_primary_back.md
@@ -1,4 +1,4 @@
-# Bring a demoted primary node back online **[PREMIUM ONLY]**
+# Bring a demoted primary node back online **(PREMIUM ONLY)**
After a failover, it is possible to fail back to the demoted **primary** node to
restore your original configuration. This process consists of two steps:
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 86182b84062..d44e141b66b 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -1,4 +1,4 @@
-# Disaster Recovery **[PREMIUM ONLY]**
+# Disaster Recovery **(PREMIUM ONLY)**
Geo replicates your database, your Git repositories, and few other assets.
We will support and replicate more data in the future, that will enable you to
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index c1a95157f8d..393326c3347 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -1,4 +1,4 @@
-# Disaster recovery for planned failover **[PREMIUM ONLY]**
+# Disaster recovery for planned failover **(PREMIUM ONLY)**
The primary use-case of Disaster Recovery is to ensure business continuity in
the event of unplanned outage, but it can be used in conjunction with a planned
diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md
index dd5e09c0dd7..0e11dffa0d6 100644
--- a/doc/administration/geo/replication/configuration.md
+++ b/doc/administration/geo/replication/configuration.md
@@ -1,4 +1,4 @@
-# Geo configuration **[PREMIUM ONLY]**
+# Geo configuration **(PREMIUM ONLY)**
## Configuring a new **secondary** node
diff --git a/doc/administration/geo/replication/database.md b/doc/administration/geo/replication/database.md
index 021ed2d9f3c..71a1ce87833 100644
--- a/doc/administration/geo/replication/database.md
+++ b/doc/administration/geo/replication/database.md
@@ -1,4 +1,4 @@
-# Geo database replication **[PREMIUM ONLY]**
+# Geo database replication **(PREMIUM ONLY)**
NOTE: **Note:**
The following steps are for Omnibus installs only. Using Geo with source-based installs was **deprecated** in GitLab 11.5.
diff --git a/doc/administration/geo/replication/docker_registry.md b/doc/administration/geo/replication/docker_registry.md
index 5b02b861c61..d5c2d2362b1 100644
--- a/doc/administration/geo/replication/docker_registry.md
+++ b/doc/administration/geo/replication/docker_registry.md
@@ -1,4 +1,4 @@
-# Docker Registry for a secondary node **[PREMIUM ONLY]**
+# Docker Registry for a secondary node **(PREMIUM ONLY)**
You can set up a [Docker Registry] on your
**secondary** Geo node that mirrors the one on the **primary** Geo node.
diff --git a/doc/administration/geo/replication/external_database.md b/doc/administration/geo/replication/external_database.md
index 452e4f490a6..85687d4a648 100644
--- a/doc/administration/geo/replication/external_database.md
+++ b/doc/administration/geo/replication/external_database.md
@@ -1,4 +1,4 @@
-# Geo with external PostgreSQL instances **[PREMIUM ONLY]**
+# Geo with external PostgreSQL instances **(PREMIUM ONLY)**
This document is relevant if you are using a PostgreSQL instance that is *not
managed by Omnibus*. This includes cloud-managed instances like AWS RDS, or
diff --git a/doc/administration/geo/replication/faq.md b/doc/administration/geo/replication/faq.md
index c527248bc72..b3580a706c3 100644
--- a/doc/administration/geo/replication/faq.md
+++ b/doc/administration/geo/replication/faq.md
@@ -1,4 +1,4 @@
-# Geo Frequently Asked Questions **[PREMIUM ONLY]**
+# Geo Frequently Asked Questions **(PREMIUM ONLY)**
## What are the minimum requirements to run Geo?
diff --git a/doc/administration/geo/replication/high_availability.md b/doc/administration/geo/replication/high_availability.md
index 61e18df2480..c737fa37077 100644
--- a/doc/administration/geo/replication/high_availability.md
+++ b/doc/administration/geo/replication/high_availability.md
@@ -1,4 +1,4 @@
-# Geo High Availability **[PREMIUM ONLY]**
+# Geo High Availability **(PREMIUM ONLY)**
This document describes a minimal reference architecture for running Geo
in a high availability configuration. If your HA setup differs from the one
diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md
index 8e1d1cb46ba..f0d329d5296 100644
--- a/doc/administration/geo/replication/index.md
+++ b/doc/administration/geo/replication/index.md
@@ -1,4 +1,4 @@
-# Geo Replication **[PREMIUM ONLY]**
+# Geo Replication **(PREMIUM ONLY)**
Geo is the solution for widely distributed development teams.
@@ -178,7 +178,7 @@ The steps below should be followed in the order they appear. **Make sure the Git
If you installed GitLab using the Omnibus packages (highly recommended):
-1. [Install GitLab Enterprise Edition](https://about.gitlab.com/installation/) on the server that will serve as the **secondary** node. Do not create an account or log in to the new **secondary** node.
+1. [Install GitLab Enterprise Edition](https://about.gitlab.com/install/) on the server that will serve as the **secondary** node. Do not create an account or log in to the new **secondary** node.
1. [Upload the GitLab License](../../../user/admin_area/license.md) on the **primary** node to unlock Geo. The license must be for [GitLab Premium](https://about.gitlab.com/pricing/) or higher.
1. [Set up the database replication](database.md) (`primary (read-write) <-> secondary (read-only)` topology).
1. [Configure fast lookup of authorized SSH keys in the database](../../operations/fast_ssh_key_lookup.md). This step is required and needs to be done on **both** the **primary** and **secondary** nodes.
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index c3c11dbaf1e..878b67a8f8e 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -1,4 +1,4 @@
-# Geo with Object storage **[PREMIUM ONLY]**
+# Geo with Object storage **(PREMIUM ONLY)**
Geo can be used in combination with Object Storage (AWS S3, or
other compatible object storage).
@@ -34,10 +34,10 @@ the bucket used by **secondary** nodes.
If you are using Google Cloud Storage, consider using
[Multi-Regional Storage](https://cloud.google.com/storage/docs/storage-classes#multi-regional).
-Or you can use the [Storage Transfer Service](https://cloud.google.com/storage/transfer/),
+Or you can use the [Storage Transfer Service](https://cloud.google.com/storage-transfer/docs/),
although this only supports daily synchronization.
For manual synchronization, or scheduled by `cron`, please have a look at:
-- [`s3cmd sync`](http://s3tools.org/s3cmd-sync)
+- [`s3cmd sync`](https://s3tools.org/s3cmd-sync)
- [`gsutil rsync`](https://cloud.google.com/storage/docs/gsutil/commands/rsync)
diff --git a/doc/administration/geo/replication/remove_geo_node.md b/doc/administration/geo/replication/remove_geo_node.md
index 6bdaad8f783..4f64e21f8ef 100644
--- a/doc/administration/geo/replication/remove_geo_node.md
+++ b/doc/administration/geo/replication/remove_geo_node.md
@@ -1,4 +1,4 @@
-# Removing secondary Geo nodes **[PREMIUM ONLY]**
+# Removing secondary Geo nodes **(PREMIUM ONLY)**
**Secondary** nodes can be removed from the Geo cluster using the Geo admin page of the **primary** node. To remove a **secondary** node:
diff --git a/doc/administration/geo/replication/security_review.md b/doc/administration/geo/replication/security_review.md
index cd54e2dc8c4..ed3f1faa93e 100644
--- a/doc/administration/geo/replication/security_review.md
+++ b/doc/administration/geo/replication/security_review.md
@@ -1,4 +1,4 @@
-# Geo security review (Q&A) **[PREMIUM ONLY]**
+# Geo security review (Q&A) **(PREMIUM ONLY)**
The following security review of the Geo feature set focuses on security
aspects of the feature as they apply to customers running their own GitLab
@@ -115,7 +115,7 @@ questions from [owasp.org](https://www.owasp.org).
### What operating systems support the application?
- Geo imposes no additional restrictions on operating system (see the
- [GitLab installation](https://about.gitlab.com/installation/) page for more
+ [GitLab installation](https://about.gitlab.com/install/) page for more
details), however we recommend using the operating systems listed in the [Geo documentation](index.md#requirements-for-running-geo).
### What details regarding required OS components and lock‐down needs have been defined?
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index c7c78407084..28abfff973d 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -1,4 +1,4 @@
-# Geo Troubleshooting **[PREMIUM ONLY]**
+# Geo Troubleshooting **(PREMIUM ONLY)**
Setting up Geo requires careful attention to details and sometimes it's easy to
miss a step.
diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md
index 1943f2230df..3ee9937774a 100644
--- a/doc/administration/geo/replication/tuning.md
+++ b/doc/administration/geo/replication/tuning.md
@@ -1,4 +1,4 @@
-# Tuning Geo **[PREMIUM ONLY]**
+# Tuning Geo **(PREMIUM ONLY)**
## Changing the sync capacity values
diff --git a/doc/administration/geo/replication/updating_the_geo_nodes.md b/doc/administration/geo/replication/updating_the_geo_nodes.md
index d56a59f4967..c27f6c78455 100644
--- a/doc/administration/geo/replication/updating_the_geo_nodes.md
+++ b/doc/administration/geo/replication/updating_the_geo_nodes.md
@@ -1,4 +1,4 @@
-# Updating the Geo nodes **[PREMIUM ONLY]**
+# Updating the Geo nodes **(PREMIUM ONLY)**
Depending on which version of Geo you are updating to/from, there may be
different steps.
diff --git a/doc/administration/geo/replication/using_a_geo_server.md b/doc/administration/geo/replication/using_a_geo_server.md
index f1f1fe48748..55b5d486676 100644
--- a/doc/administration/geo/replication/using_a_geo_server.md
+++ b/doc/administration/geo/replication/using_a_geo_server.md
@@ -1,6 +1,6 @@
[//]: # (Please update EE::GitLab::GeoGitAccess::GEO_SERVER_DOCS_URL if this file is moved)
-# Using a Geo Server **[PREMIUM ONLY]**
+# Using a Geo Server **(PREMIUM ONLY)**
After you set up the [database replication and configure the Geo nodes][req], use your closest GitLab node as you would a normal standalone GitLab instance.
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index a3cbc4272f0..4407facfca9 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -91,7 +91,7 @@ your GitLab installation has two repository storages, `default` and
First install Gitaly using either Omnibus or from source.
-Omnibus: [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+Omnibus: [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page but
**_do not_** provide the `EXTERNAL_URL=` value.
@@ -220,7 +220,7 @@ network, firewall, or name resolution problem preventing your GitLab
server from reaching the Gitaly server then all Gitaly requests will
fail.
-Additionally, you need to
+Additionally, you need to
[disable Rugged if previously manually enabled](../high_availability/nfs.md#improving-nfs-performance-with-gitlab).
We assume that your Gitaly server can be reached at
@@ -247,8 +247,10 @@ gitlab:
repositories:
storages:
default:
+ path: /mnt/gitlab/default/repositories
gitaly_address: tcp://gitaly.internal:8075
storage1:
+ path: /mnt/gitlab/storage1/repositories
gitaly_address: tcp://gitaly.internal:8075
gitaly:
@@ -293,8 +295,8 @@ sum(rate(gitaly_connections_total[5m])) by (type)
```ruby
# /etc/gitlab/gitlab.rb
git_data_dirs({
- 'default' => { 'path' => '/mnt/gitlab/default', 'gitaly_address' => 'tls://gitaly.internal:9999' },
- 'storage1' => { 'path' => '/mnt/gitlab/storage1', 'gitaly_address' => 'tls://gitaly.internal:9999' },
+ 'default' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
+ 'storage1' => { 'gitaly_address' => 'tls://gitaly.internal:9999' },
})
gitlab_rails['gitaly_token'] = 'abc123secret'
@@ -434,17 +436,17 @@ particular machine.
## Eliminating NFS altogether
-If you are planning to use Gitaly without NFS for your storage needs
-and want to eliminate NFS from your environment altogether, there are
+If you are planning to use Gitaly without NFS for your storage needs
+and want to eliminate NFS from your environment altogether, there are
a few things that you need to do:
1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
- 1. Configure [database lookup of SSH keys](https://docs.gitlab.com/ce/administration/operations/fast_ssh_key_lookup.html)
+ 1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md)
to eliminate the need for a shared authorized_keys file.
- 1. Configure [object storage for job artifacts](https://docs.gitlab.com/ce/administration/job_artifacts.html#using-object-storage)
- including [live tracing](https://docs.gitlab.com/ce/administration/job_traces.html#new-live-trace-architecture).
- 1. Configure [object storage for LFS objects](https://docs.gitlab.com/ce/workflow/lfs/lfs_administration.html#storing-lfs-objects-in-remote-object-storage).
- 1. Configure [object storage for uploads](https://docs.gitlab.com/ce/administration/uploads.html#using-object-storage-core-only).
+ 1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage)
+ including [live tracing](../job_traces.md#new-live-trace-architecture).
+ 1. Configure [object storage for LFS objects](../../workflow/lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
+ 1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only).
NOTE: **Note:** One current feature of GitLab still requires a shared directory (NFS): [GitLab Pages](../../user/project/pages/index.md).
There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196)
diff --git a/doc/administration/high_availability/consul.md b/doc/administration/high_availability/consul.md
index 056b7fc15d9..49199b659bc 100644
--- a/doc/administration/high_availability/consul.md
+++ b/doc/administration/high_availability/consul.md
@@ -1,11 +1,77 @@
-# Working with the bundled Consul service **[PREMIUM ONLY]**
+# Working with the bundled Consul service **(PREMIUM ONLY)**
## Overview
-As part of its High Availability stack, GitLab Premium includes a bundled version of [Consul](http://consul.io) that can be managed through `/etc/gitlab/gitlab.rb`.
+As part of its High Availability stack, GitLab Premium includes a bundled version of [Consul](https://www.consul.io/) that can be managed through `/etc/gitlab/gitlab.rb`.
A Consul cluster consists of multiple server agents, as well as client agents that run on other nodes which need to talk to the consul cluster.
+## Prerequisites
+
+First, make sure to [download/install](https://about.gitlab.com/install/)
+GitLab Omnibus **on each node**.
+
+Choose an installation method, then make sure you complete steps:
+
+1. Install and configure the necessary dependencies.
+1. Add the GitLab package repository and install the package.
+
+When installing the GitLab package, do not supply `EXTERNAL_URL` value.
+
+## Configuring the Consul nodes
+
+On each Consul node perform the following:
+
+1. Make sure you collect [`CONSUL_SERVER_NODES`](database.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step, before executing the next step.
+
+1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
+
+ ```ruby
+ # Disable all components except Consul
+ roles ['consul_role']
+
+ # START user configuration
+ # Replace placeholders:
+ #
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses gathered for CONSUL_SERVER_NODES
+ consul['configuration'] = {
+ server: true,
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
+ }
+
+ # Disable auto migrations
+ gitlab_rails['auto_migrate'] = false
+ #
+ # END user configuration
+ ```
+
+ > `consul_role` was introduced with GitLab 10.3
+
+1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes
+ to take effect.
+
+### Consul checkpoint
+
+Before moving on, make sure Consul is configured correctly. Run the following
+command to verify all server nodes are communicating:
+
+```sh
+/opt/gitlab/embedded/bin/consul members
+```
+
+The output should be similar to:
+
+```
+Node Address Status Type Build Protocol DC
+CONSUL_NODE_ONE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
+CONSUL_NODE_TWO XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
+CONSUL_NODE_THREE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
+```
+
+If any of the nodes isn't `alive` or if any of the three nodes are missing,
+check the [Troubleshooting section](#troubleshooting) before proceeding.
+
## Operations
### Checking cluster membership
diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md
index 20bbfdb2603..c32a73080ff 100644
--- a/doc/administration/high_availability/database.md
+++ b/doc/administration/high_availability/database.md
@@ -1,6 +1,6 @@
# Configuring PostgreSQL for Scaling and High Availability
-## Provide your own PostgreSQL instance **[CORE ONLY]**
+## Provide your own PostgreSQL instance **(CORE ONLY)**
If you're hosting GitLab on a cloud provider, you can optionally use a
managed service for PostgreSQL. For example, AWS offers a managed Relational
@@ -21,17 +21,17 @@ This section is relevant for [Scaled Architecture](README.md#scalable-architectu
environments including [Basic Scaling](README.md#basic-scaling) and
[Full Scaling](README.md#full-scaling).
-### Provide your own PostgreSQL instance **[CORE ONLY]**
+### Provide your own PostgreSQL instance **(CORE ONLY)**
If you want to use your own deployed PostgreSQL instance(s),
see [Provide your own PostgreSQL instance](#provide-your-own-postgresql-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled PostgreSQL.
-### Standalone PostgreSQL using GitLab Omnibus **[CORE ONLY]**
+### Standalone PostgreSQL using GitLab Omnibus **(CORE ONLY)**
1. SSH into the PostgreSQL server.
-1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page.
- Do not complete any other steps on the download page.
1. Generate a password hash for PostgreSQL. This assumes you will use the default
@@ -97,14 +97,14 @@ environments including [Horizontal](README.md#horizontal),
[Hybrid](README.md#hybrid), and
[Fully Distributed](README.md#fully-distributed).
-### Provide your own PostgreSQL instance **[CORE ONLY]**
+### Provide your own PostgreSQL instance **(CORE ONLY)**
If you want to use your own deployed PostgreSQL instance(s),
see [Provide your own PostgreSQL instance](#provide-your-own-postgresql-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled PostgreSQL.
-### High Availability with GitLab Omnibus **[PREMIUM ONLY]**
+### High Availability with GitLab Omnibus **(PREMIUM ONLY)**
> Important notes:
>
@@ -281,68 +281,17 @@ Few notes on the service itself:
#### Installing Omnibus GitLab
-First, make sure to [download/install](https://about.gitlab.com/installation)
+First, make sure to [download/install](https://about.gitlab.com/install/)
GitLab Omnibus **on each node**.
Make sure you install the necessary dependencies from step 1,
add GitLab package repository from step 2.
When installing the GitLab package, do not supply `EXTERNAL_URL` value.
-#### Configuring the Consul nodes
-
-On each Consul node perform the following:
-
-1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information) before executing the next step.
-
-1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
-
- ```ruby
- # Disable all components except Consul
- roles ['consul_role']
-
- # START user configuration
- # Replace placeholders:
- #
- # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
- # with the addresses gathered for CONSUL_SERVER_NODES
- consul['configuration'] = {
- server: true,
- retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
- }
-
- # Disable auto migrations
- gitlab_rails['auto_migrate'] = false
- #
- # END user configuration
- ```
-
- > `consul_role` was introduced with GitLab 10.3
-
-1. [Reconfigure GitLab] for the changes to take effect.
-
-##### Consul Checkpoint
-
-Before moving on, make sure Consul is configured correctly. Run the following
-command to verify all server nodes are communicating:
-
-```sh
-/opt/gitlab/embedded/bin/consul members
-```
-
-The output should be similar to:
-
-```
-Node Address Status Type Build Protocol DC
-CONSUL_NODE_ONE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
-CONSUL_NODE_TWO XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
-CONSUL_NODE_THREE XXX.XXX.XXX.YYY:8301 alive server 0.9.2 2 gitlab_consul
-```
-
-If any of the nodes isn't `alive` or if any of the three nodes are missing,
-check the [Troubleshooting section](#troubleshooting) before proceeding.
#### Configuring the Database nodes
+1. Make sure to [configure the Consul nodes](consul.md).
1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information), [`POSTGRESQL_PASSWORD_HASH`](#postgresql-information), the [number of db nodes](#postgresql-information), and the [network address](#network-information) before executing the next step.
1. On the master database node, edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
@@ -570,7 +519,7 @@ Check the [Troubleshooting section](#troubleshooting) before proceeding.
1. [Reconfigure GitLab] for the changes to take effect.
-1. Create a `.pgpass` file so Consule is able to
+1. Create a `.pgpass` file so Consul is able to
reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked:
```sh
@@ -1109,7 +1058,7 @@ If you enable Monitoring, it must be enabled on **all** database servers.
## Troubleshooting
-#### Consul and PostgreSQL changes not taking effect.
+### Consul and PostgreSQL changes not taking effect.
Due to the potential impacts, `gitlab-ctl reconfigure` only reloads Consul and PostgreSQL, it will not restart the services. However, not all changes can be activated by reloading.
@@ -1119,7 +1068,7 @@ For PostgreSQL, it is usually safe to restart the master node by default. Automa
On the consul server nodes, it is important to restart the consul service in a controlled fashion. Read our [consul documentation](consul.md#restarting-the-server-cluster) for instructions on how to restart the service.
-#### `gitlab-ctl repmgr-check-master` command produces errors
+### `gitlab-ctl repmgr-check-master` command produces errors
If this command displays errors about database permissions it is likely that something failed during
install, resulting in the `gitlab-consul` database user getting incorrect permissions. Follow these
@@ -1134,7 +1083,7 @@ steps to fix the problem:
Now there should not be errors. If errors still occur then there is another problem.
-#### PGBouncer error `ERROR: pgbouncer cannot connect to server`
+### PGBouncer error `ERROR: pgbouncer cannot connect to server`
You may get this error when running `gitlab-rake gitlab:db:configure` or you
may see the error in the PGBouncer log file.
@@ -1162,7 +1111,7 @@ postgresql['trust_auth_cidr_addresses'] = %w(123.123.123.123/32 <other_cidrs>)
[Reconfigure GitLab] for the changes to take effect.
-#### Issues with other components
+### Issues with other components
If you're running into an issue with a component not outlined here, be sure to check the troubleshooting section of their specific documentation page.
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 3045be616a6..9b1b7142e83 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -125,7 +125,7 @@ need some extra configuration.
from running on upgrade. Only the primary GitLab application server should
handle migrations.
-1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on
+1. **Recommended** Configure host keys. Copy the contents (primary and public keys) of `/etc/ssh/` on
the primary application server to `/etc/ssh` on all secondary servers. This
prevents false man-in-the-middle-attack alerts when accessing servers in your
High Availability cluster behind a load balancer.
diff --git a/doc/administration/high_availability/monitoring_node.md b/doc/administration/high_availability/monitoring_node.md
index ef415dde10a..385e7441ac9 100644
--- a/doc/administration/high_availability/monitoring_node.md
+++ b/doc/administration/high_availability/monitoring_node.md
@@ -12,7 +12,7 @@ The steps below are the minimum necessary to configure a Monitoring node running
Omnibus:
1. SSH into the Monitoring node.
-1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+1. [Download/install](https://about.gitlab.com/install) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page.
- Do not complete any other steps on the download page.
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index 874525dd836..27310c59755 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -1,6 +1,6 @@
# Configuring Redis for Scaling and High Availability
-## Provide your own Redis instance **[CORE ONLY]**
+## Provide your own Redis instance **(CORE ONLY)**
The following are the requirements for providing your own Redis instance:
@@ -20,14 +20,14 @@ This section is relevant for [Scaled Architecture](README.md#scalable-architectu
environments including [Basic Scaling](README.md#basic-scaling) and
[Full Scaling](README.md#full-scaling).
-### Provide your own Redis instance **[CORE ONLY]**
+### Provide your own Redis instance **(CORE ONLY)**
If you want to use your own deployed Redis instance(s),
see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled Redis.
-### Standalone Redis using GitLab Omnibus **[CORE ONLY]**
+### Standalone Redis using GitLab Omnibus **(CORE ONLY)**
The GitLab Omnibus package can be used to configure a standalone Redis server.
In this configuration Redis is not highly available, and represents a single
@@ -41,7 +41,7 @@ The steps below are the minimum necessary to configure a Redis server with
Omnibus:
1. SSH into the Redis server.
-1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page.
- Do not complete any other steps on the download page.
@@ -89,14 +89,14 @@ environments including [Horizontal](README.md#horizontal),
[Hybrid](README.md#hybrid), and
[Fully Distributed](README.md#fully-distributed).
-### Provide your own Redis instance **[CORE ONLY]**
+### Provide your own Redis instance **(CORE ONLY)**
If you want to use your own deployed Redis instance(s),
see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled Redis.
-### High Availability with GitLab Omnibus **[PREMIUM ONLY]**
+### High Availability with GitLab Omnibus **(PREMIUM ONLY)**
> Experimental Redis Sentinel support was [introduced in GitLab 8.11][ce-1877].
Starting with 8.14, Redis Sentinel is no longer experimental.
@@ -357,7 +357,7 @@ The prerequisites for a HA Redis setup are the following:
### Step 1. Configuring the master Redis instance
1. SSH into the **master** Redis server.
-1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page.
- Make sure you select the correct Omnibus package, with the same version
and type (Community, Enterprise editions) of your current install.
@@ -400,7 +400,7 @@ The prerequisites for a HA Redis setup are the following:
### Step 2. Configuring the slave Redis instances
1. SSH into the **slave** Redis server.
-1. [Download/install](https://about.gitlab.com/installation) the Omnibus GitLab
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
package you want using **steps 1 and 2** from the GitLab downloads page.
- Make sure you select the correct Omnibus package, with the same version
and type (Community, Enterprise editions) of your current install.
diff --git a/doc/administration/audit_log.png b/doc/administration/img/audit_log.png
index d4f4c2abf38..d4f4c2abf38 100644
--- a/doc/administration/audit_log.png
+++ b/doc/administration/img/audit_log.png
Binary files differ
diff --git a/doc/administration/auditor_access_form.png b/doc/administration/img/auditor_access_form.png
index c179a7d3b0a..c179a7d3b0a 100644
--- a/doc/administration/auditor_access_form.png
+++ b/doc/administration/img/auditor_access_form.png
Binary files differ
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 8271c579f5b..73a39a6dd35 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -4,14 +4,14 @@ GitLab has several features based on receiving incoming emails:
- [Reply by Email](reply_by_email.md): allow GitLab users to comment on issues
and merge requests by replying to notification emails.
-- [New issue by email](../user/project/issues/create_new_issue.md#new-issue-via-email):
+- [New issue by email](../user/project/issues/managing_issues.md#new-issue-via-email):
allow GitLab users to create a new issue by sending an email to a
user-specific email address.
- [New merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email):
allow GitLab users to create a new merge request by sending an email to a
user-specific email address.
- [Service Desk](../user/project/service_desk.md): provide e-mail support to
- your customers through GitLab. **[PREMIUM]**
+ your customers through GitLab. **(PREMIUM)**
## Requirements
@@ -102,16 +102,16 @@ for a real-world example of this exploit.
1. Reconfigure GitLab for the changes to take effect:
- ```sh
- sudo gitlab-ctl reconfigure
- sudo gitlab-ctl restart
- ```
+ ```sh
+ sudo gitlab-ctl reconfigure
+ sudo gitlab-ctl restart
+ ```
1. Verify that everything is configured correctly:
- ```sh
- sudo gitlab-rake gitlab:incoming_email:check
- ```
+ ```sh
+ sudo gitlab-rake gitlab:incoming_email:check
+ ```
Reply by email should now be working.
@@ -119,31 +119,31 @@ Reply by email should now be working.
1. Go to the GitLab installation directory:
- ```sh
- cd /home/git/gitlab
- ```
+ ```sh
+ cd /home/git/gitlab
+ ```
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below).
1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
- ```sh
- sudo mkdir -p /etc/default
- echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
- ```
+ ```sh
+ sudo mkdir -p /etc/default
+ echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
+ ```
1. Restart GitLab:
- ```sh
- sudo service gitlab restart
- ```
+ ```sh
+ sudo service gitlab restart
+ ```
1. Verify that everything is configured correctly:
- ```sh
- sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
- ```
+ ```sh
+ sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
+ ```
Reply by email should now be working.
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 602eecb9746..00c8863f200 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -2,7 +2,7 @@
description: 'Learn how to install, configure, update, and maintain your GitLab instance.'
---
-# Administrator documentation **[CORE ONLY]**
+# Administrator documentation **(CORE ONLY)**
Learn how to administer your self-managed GitLab instance.
@@ -11,7 +11,7 @@ GitLab has two product distributions available through [different subscriptions]
- The open source [GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce).
- The open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee).
-You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/).
+You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/install/ce-or-ee/).
However, the features you'll have access to depend on the subscription you choose
(Core, Starter, Premium, or Ultimate).
@@ -32,14 +32,14 @@ Learn how to install, configure, update, and maintain your GitLab instance.
### Installing GitLab
- [Install](../install/README.md): Requirements, directory structures, and installation methods.
- - [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. **[STARTER ONLY]**
- - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **[STARTER ONLY]**
+ - [Database load balancing](database_load_balancing.md): Distribute database queries among multiple database servers. **(STARTER ONLY)**
+ - [Omnibus support for log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only) **(STARTER ONLY)**
- [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability.
- [Installing GitLab HA on Amazon Web Services (AWS)](../install/aws/index.md): Set up GitLab High Availability on Amazon AWS.
-- [Geo](geo/replication/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. **[PREMIUM ONLY]**
-- [Disaster Recovery](geo/disaster_recovery/index.md): Quickly fail-over to a different site with minimal effort in a disaster situation. **[PREMIUM ONLY]**
-- [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry. **[PREMIUM ONLY]**
-- [Add License](../user/admin_area/license.md): Upload a license at install time to unlock features that are in paid tiers of GitLab. **[STARTER ONLY]**
+- [Geo](geo/replication/index.md): Replicate your GitLab instance to other geographic locations as a read-only fully operational version. **(PREMIUM ONLY)**
+- [Disaster Recovery](geo/disaster_recovery/index.md): Quickly fail-over to a different site with minimal effort in a disaster situation. **(PREMIUM ONLY)**
+- [Pivotal Tile](../install/pivotal/index.md): Deploy GitLab as a pre-configured appliance using Ops Manager (BOSH) for Pivotal Cloud Foundry. **(PREMIUM ONLY)**
+- [Add License](../user/admin_area/license.md): Upload a license at install time to unlock features that are in paid tiers of GitLab. **(STARTER ONLY)**
### Configuring GitLab
@@ -60,9 +60,9 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
- [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage.
- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI.
-- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to empower GitLab's Advanced Global Search. Useful when you deal with a huge amount of data. **[STARTER ONLY]**
-- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md) **[PREMIUM ONLY]**
-- [Upload a license](../user/admin_area/license.md): Upload a license to unlock features that are in paid tiers of GitLab. **[STARTER ONLY]**
+- [Elasticsearch](../integration/elasticsearch.md): Enable Elasticsearch to empower GitLab's Advanced Global Search. Useful when you deal with a huge amount of data. **(STARTER ONLY)**
+- [External Classification Policy Authorization](../user/admin_area/settings/external_authorization.md) **(PREMIUM ONLY)**
+- [Upload a license](../user/admin_area/license.md): Upload a license to unlock features that are in paid tiers of GitLab. **(STARTER ONLY)**
- [Admin Area](../user/admin_area/index.md): for self-managed instance-wide configuration and maintenance.
#### Customizing GitLab's appearance
@@ -72,7 +72,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description.
- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project.
-- [Additional custom email text](../user/admin_area/settings/email.md#custom-additional-text-premium-only): Add additional custom text to emails sent from GitLab. **[PREMIUM ONLY]**
+- [Additional custom email text](../user/admin_area/settings/email.md#custom-additional-text-premium-only): Add additional custom text to emails sent from GitLab. **(PREMIUM ONLY)**
### Maintaining GitLab
@@ -107,17 +107,17 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or whitelist only specific domains.
- [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS).
- [Authentication and Authorization](auth/README.md): Configure external authentication with LDAP, SAML, CAS and additional providers.
- - [Sync LDAP](auth/ldap-ee.md) **[STARTER ONLY]**
- - [Kerberos authentication](../integration/kerberos.md) **[STARTER ONLY]**
+ - [Sync LDAP](auth/ldap-ee.md) **(STARTER ONLY)**
+ - [Kerberos authentication](../integration/kerberos.md) **(STARTER ONLY)**
- See also other [authentication](../topics/authentication/index.md#gitlab-administrators) topics (for example, enforcing 2FA).
-- [Email users](../tools/email.md): Email GitLab users from within GitLab. **[STARTER ONLY]**
+- [Email users](../tools/email.md): Email GitLab users from within GitLab. **(STARTER ONLY)**
- [User Cohorts](../user/admin_area/user_cohorts.md): Display the monthly cohorts of new users and their activities over time.
- [Audit logs and events](audit_events.md): View the changes made within the GitLab server for:
- - Groups and projects. **[STARTER]**
- - Instances. **[PREMIUM ONLY]**
-- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **[PREMIUM ONLY]**
+ - Groups and projects. **(STARTER)**
+ - Instances. **(PREMIUM ONLY)**
+- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **(PREMIUM ONLY)**
- [Incoming email](incoming_email.md): Configure incoming emails to allow
- users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/create_new_issue.md#new-issue-via-email) and
+ users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#new-issue-via-email) and
[merge requests by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email), and to enable [Service Desk](../user/project/service_desk.md).
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
@@ -131,15 +131,15 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service.
- [Default labels](../user/admin_area/labels.md): Create labels that will be automatically added to every new project.
- [Restrict the use of public or internal projects](../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects): Restrict the use of visibility levels for users when they create a project or a snippet.
-- [Custom project templates](../user/admin_area/custom_project_templates.md): Configure a set of projects to be used as custom templates when creating a new project. **[PREMIUM ONLY]**
-- [Packages](packages.md): Enable GitLab to act as a Maven repository or NPM registry. **[PREMIUM ONLY]**
+- [Custom project templates](../user/admin_area/custom_project_templates.md): Configure a set of projects to be used as custom templates when creating a new project. **(PREMIUM ONLY)**
+- [Packages](packages.md): Enable GitLab to act as a Maven repository or NPM registry. **(PREMIUM ONLY)**
### Repository settings
- [Repository checks](repository_checks.md): Periodic Git repository checks.
- [Repository storage paths](repository_storage_paths.md): Manage the paths used to store repositories.
- [Repository storage rake tasks](raketasks/storage.md): A collection of rake tasks to list and migrate existing projects and attachments associated with it from Legacy storage to Hashed storage.
-- [Limit repository size](../user/admin_area/settings/account_and_limit_settings.md): Set a hard limit for your repositories' size. **[STARTER ONLY]**
+- [Limit repository size](../user/admin_area/settings/account_and_limit_settings.md): Set a hard limit for your repositories' size. **(STARTER ONLY)**
## Continuous Integration settings
@@ -148,7 +148,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Job artifacts](job_artifacts.md): Enable, disable, and configure job artifacts (a set of files and directories which are outputted by a job when it completes successfully).
- [Job traces](job_traces.md): Information about the job traces (logs).
- [Register Shared and specific Runners](../ci/runners/README.md#registering-a-shared-runner): Learn how to register and configure Shared and specific Runners to your own instance.
-- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota-starter-only): Limit the usage of pipeline minutes for Shared Runners. **[STARTER ONLY]**
+- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota-starter-only): Limit the usage of pipeline minutes for Shared Runners. **(STARTER ONLY)**
- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enablingdisabling-auto-devops): Enable or disable Auto DevOps for your instance.
## Git configuration options
@@ -178,7 +178,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
## Analytics
-- [Pseudonymizer](pseudonymizer.md): Export data from GitLab's database to CSV files in a secure way. **[ULTIMATE]**
+- [Pseudonymizer](pseudonymizer.md): Export data from GitLab's database to CSV files in a secure way. **(ULTIMATE)**
## Troubleshooting
diff --git a/doc/administration/instance_review.md b/doc/administration/instance_review.md
index b1244f44e95..ab6a4646a71 100644
--- a/doc/administration/instance_review.md
+++ b/doc/administration/instance_review.md
@@ -1,4 +1,4 @@
-# Instance Review **[CORE ONLY]**
+# Instance Review **(CORE ONLY)**
> [Introduced][6995] in [GitLab Core][ee] 11.3.
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 82e0c14ffc2..8de7b0bc57e 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -94,7 +94,7 @@ our AsciiDoc snippets, wikis and repos using delimited blocks:
Alice -> Bob: Go Away
```
- You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.python.org/pypi/sphinxcontrib-plantuml), but please note that we currently only support the `caption` option.
+ You can also use the `uml::` directive for compatibility with [sphinxcontrib-plantuml](https://pypi.org/project/sphinxcontrib-plantuml/), but please note that we currently only support the `caption` option.
The above blocks will be converted to an HTML img tag with source pointing to the
PlantUML instance. If the PlantUML server is correctly configured, this should
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index 160da47c780..e1bbabb2878 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -1,4 +1,4 @@
-# Issue closing pattern
+# Issue closing pattern **(CORE ONLY)**
>**Note:**
This is the administration documentation.
@@ -27,9 +27,10 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
1. Change the value of `gitlab_rails['gitlab_issue_closing_pattern']` to a regular
expression of your liking:
- ```ruby
- gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
- ```
+ ```ruby
+ gitlab_rails['gitlab_issue_closing_pattern'] = "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
+ ```
+
1. [Reconfigure] GitLab for the changes to take effect.
**For installations from source**
@@ -37,13 +38,13 @@ Because Rubular doesn't understand `%{issue_ref}`, you can replace this by
1. Open `gitlab.yml` with your editor.
1. Change the value of `issue_closing_pattern`:
- ```yaml
- issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
- ```
+ ```yaml
+ issue_closing_pattern: "\b((?:[Cc]los(?:e[sd]|ing)|\b[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)"
+ ```
1. [Restart] GitLab for the changes to take effect.
[gitlab.yml.example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example
[reconfigure]: restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: restart_gitlab.md#installations-from-source
-[user documentation]: ../user/project/issues/automatic_issue_closing.md
+[user documentation]: ../user/project/issues/managing_issues.md#closing-issues-automatically
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 05e15fc303b..9df7b2526e2 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,7 +1,5 @@
# Jobs artifacts administration
-> **Notes:**
->
> - Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`.
> - Starting with GitLab 8.17, builds are renamed to jobs.
@@ -21,9 +19,9 @@ To disable artifacts site-wide, follow the steps below.
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['artifacts_enabled'] = false
- ```
+ ```ruby
+ gitlab_rails['artifacts_enabled'] = false
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -33,10 +31,10 @@ To disable artifacts site-wide, follow the steps below.
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
- ```yaml
- artifacts:
- enabled: false
- ```
+ ```yaml
+ artifacts:
+ enabled: false
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -61,9 +59,9 @@ _The artifacts are stored by default in
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts"
- ```
+ ```ruby
+ gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -77,18 +75,16 @@ _The artifacts are stored by default in
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
- ```yaml
- artifacts:
- enabled: true
- path: /mnt/storage/artifacts
- ```
+ ```yaml
+ artifacts:
+ enabled: true
+ path: /mnt/storage/artifacts
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
### Using object storage
-> **Notes:**
->
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
> [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
> - Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
@@ -141,35 +137,35 @@ _The artifacts are stored by default in
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
the values you want:
- ```ruby
- gitlab_rails['artifacts_enabled'] = true
- gitlab_rails['artifacts_object_store_enabled'] = true
- gitlab_rails['artifacts_object_store_remote_directory'] = "artifacts"
- gitlab_rails['artifacts_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
- }
- ```
-
- NOTE: For GitLab 9.4+, if you are using AWS IAM profiles, be sure to omit the
- AWS access key and secret access key/value pairs. For example:
-
- ```ruby
- gitlab_rails['artifacts_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'use_iam_profile' => true
- }
- ```
+ ```ruby
+ gitlab_rails['artifacts_enabled'] = true
+ gitlab_rails['artifacts_object_store_enabled'] = true
+ gitlab_rails['artifacts_object_store_remote_directory'] = "artifacts"
+ gitlab_rails['artifacts_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
+ }
+ ```
+
+ NOTE: For GitLab 9.4+, if you are using AWS IAM profiles, be sure to omit the
+ AWS access key and secret access key/value pairs. For example:
+
+ ```ruby
+ gitlab_rails['artifacts_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'use_iam_profile' => true
+ }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
- ```bash
- gitlab-rake gitlab:artifacts:migrate
- ```
+ ```bash
+ gitlab-rake gitlab:artifacts:migrate
+ ```
---
@@ -181,25 +177,25 @@ _The artifacts are stored by default in
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- artifacts:
- enabled: true
- object_store:
- enabled: true
- remote_directory: "artifacts" # The bucket name
- connection:
- provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACCESS_KEY_ID
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- region: eu-central-1
- ```
+ ```yaml
+ artifacts:
+ enabled: true
+ object_store:
+ enabled: true
+ remote_directory: "artifacts" # The bucket name
+ connection:
+ provider: AWS # Only AWS supported at the moment
+ aws_access_key_id: AWS_ACCESS_KEY_ID
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ region: eu-central-1
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
- ```bash
- sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production
- ```
+ ```bash
+ sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production
+ ```
## Expiring artifacts
@@ -217,9 +213,9 @@ steps below.
1. Edit `/etc/gitlab/gitlab.rb` and comment out or add the following line
- ```ruby
- gitlab_rails['expire_build_artifacts_worker_cron'] = "50 * * * *"
- ```
+ ```ruby
+ gitlab_rails['expire_build_artifacts_worker_cron'] = "50 * * * *"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -230,10 +226,10 @@ steps below.
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- expire_build_artifacts_worker:
- cron: "50 * * * *"
- ```
+ ```yaml
+ expire_build_artifacts_worker:
+ cron: "50 * * * *"
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -250,15 +246,15 @@ you can flip the feature flag from a Rails console.
1. Enter the Rails console:
- ```sh
- sudo gitlab-rails console
- ```
+ ```sh
+ sudo gitlab-rails console
+ ```
1. Flip the switch and disable it:
- ```ruby
- Feature.enable('ci_disable_validates_dependencies')
- ```
+ ```ruby
+ Feature.enable('ci_disable_validates_dependencies')
+ ```
---
@@ -266,16 +262,16 @@ you can flip the feature flag from a Rails console.
1. Enter the Rails console:
- ```sh
- cd /home/git/gitlab
- RAILS_ENV=production sudo -u git -H bundle exec rails console
- ```
+ ```sh
+ cd /home/git/gitlab
+ RAILS_ENV=production sudo -u git -H bundle exec rails console
+ ```
1. Flip the switch and disable it:
- ```ruby
- Feature.enable('ci_disable_validates_dependencies')
- ```
+ ```ruby
+ Feature.enable('ci_disable_validates_dependencies')
+ ```
## Set the maximum file size of the artifacts
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index aa9d87562a3..957916893d7 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -25,11 +25,11 @@ To change the location where the job logs will be stored, follow the steps below
**In Omnibus installations:**
-1. Edit `/etc/gitlab/gitlab.rb` and add or amend the following line:
+1. Edit `/etc/gitlab/gitlab.rb` and add or amend the following line:
- ```
- gitlab_ci['builds_directory'] = '/mnt/to/gitlab-ci/builds'
- ```
+ ```ruby
+ gitlab_ci['builds_directory'] = '/mnt/to/gitlab-ci/builds'
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -39,12 +39,12 @@ To change the location where the job logs will be stored, follow the steps below
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
- ```yaml
- gitlab_ci:
- # The location where build traces are stored (default: builds/).
- # Relative paths are relative to Rails.root.
- builds_path: path/to/builds/
- ```
+ ```yaml
+ gitlab_ci:
+ # The location where build traces are stored (default: builds/).
+ # Relative paths are relative to Rails.root.
+ builds_path: path/to/builds/
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
@@ -67,24 +67,24 @@ To archive those legacy job traces, please follow the instruction below.
1. Execute the following command
- ```bash
- gitlab-rake gitlab:traces:archive
- ```
+ ```bash
+ gitlab-rake gitlab:traces:archive
+ ```
- After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes)
- for migrating job trace files from local storage to object storage.
- It could take time to complete the all migration jobs. You can check the progress by the following command
+ After you executed this task, GitLab instance queues up Sidekiq jobs (asynchronous processes)
+ for migrating job trace files from local storage to object storage.
+ It could take time to complete the all migration jobs. You can check the progress by the following command
- ```bash
- sudo gitlab-rails console
- ```
+ ```bash
+ sudo gitlab-rails console
+ ```
- ```bash
- [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace']
- => 100
- ```
+ ```bash
+ [1] pry(main)> Sidekiq::Stats.new.queues['pipeline_background:archive_trace']
+ => 100
+ ```
- If the count becomes zero, the archiving processes are done
+ If the count becomes zero, the archiving processes are done
## How to migrate archived job traces to object storage
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 9921ffd8ea0..5a2f389d298 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -4,7 +4,7 @@ GitLab has an advanced log system where everything is logged so that you
can analyze your instance using various system log files. In addition to
system log files, GitLab Enterprise Edition comes with Audit Events.
Find more about them [in Audit Events
-documentation](https://docs.gitlab.com/ee/administration/audit_events.html)
+documentation](audit_events.md)
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.
@@ -288,6 +288,9 @@ installations from source.
It logs information whenever [Rack Attack] registers an abusive request.
+NOTE: **Note:**
+From [%12.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/62756), user id and username are available on this log.
+
## `graphql_json.log`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/59587) in GitLab 12.0.
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 5e9ba4f640f..99cd9051778 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -1,4 +1,4 @@
-# Merge request diffs storage **[CORE ONLY]**
+# Merge request diffs storage **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52568) in GitLab 11.8.
@@ -10,7 +10,7 @@ By default, merge request diffs are stored in the database, in a table named
`merge_request_diff_files`. Larger installations may find this table grows too
large, in which case, switching to external storage is recommended.
-### Using external storage
+## Using external storage
Merge request diffs can be stored on disk, or in object storage. In general, it
is better to store the diffs in the database than on disk.
@@ -21,18 +21,18 @@ To enable external storage of merge request diffs, follow the instructions below
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['external_diffs_enabled'] = true
- ```
+ ```ruby
+ gitlab_rails['external_diffs_enabled'] = true
+ ```
1. _The external diffs will be stored in in
`/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path,
for example, to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb`
and add the following line:
- ```ruby
- gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs"
- ```
+ ```ruby
+ gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs"
+ ```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
@@ -41,31 +41,31 @@ To enable external storage of merge request diffs, follow the instructions below
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- external_diffs:
- enabled: true
- ```
+ ```yaml
+ external_diffs:
+ enabled: true
+ ```
-1. _The external diffs will be stored in
+1. _The external diffs will be stored in
`/home/git/gitlab/shared/external-diffs`._ To change the path, for example,
to `/mnt/storage/external-diffs`, edit `/home/git/gitlab/config/gitlab.yml`
and add or amend the following lines:
- ```yaml
- external_diffs:
- enabled: true
- storage_path: /mnt/storage/external-diffs
- ```
+ ```yaml
+ external_diffs:
+ enabled: true
+ storage_path: /mnt/storage/external-diffs
+ ```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
-### Using object storage
+## Using object storage
Instead of storing the external diffs on disk, we recommended the use of an object
store like AWS S3 instead. This configuration relies on valid AWS credentials to
be configured already.
-### Object Storage Settings
+## Object Storage Settings
For source installations, these settings are nested under `external_diffs:` and
then `object_store:`. On Omnibus installations, they are prefixed by
@@ -80,7 +80,7 @@ then `object_store:`. On Omnibus installations, they are prefixed by
| `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
| `connection` | Various connection options described below | |
-#### S3 compatible connection settings
+### S3 compatible connection settings
The connection settings match those provided by [Fog](https://github.com/fog), and are as follows:
@@ -101,28 +101,28 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
the values you want:
- ```ruby
- gitlab_rails['external_diffs_enabled'] = true
- gitlab_rails['external_diffs_object_store_enabled'] = true
- gitlab_rails['external_diffs_object_store_remote_directory'] = "external-diffs"
- gitlab_rails['external_diffs_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
- }
- ```
-
- Note that, if you are using AWS IAM profiles, be sure to omit the
- AWS access key and secret access key/value pairs. For example:
-
- ```ruby
- gitlab_rails['external_diffs_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'use_iam_profile' => true
- }
- ```
+ ```ruby
+ gitlab_rails['external_diffs_enabled'] = true
+ gitlab_rails['external_diffs_object_store_enabled'] = true
+ gitlab_rails['external_diffs_object_store_remote_directory'] = "external-diffs"
+ gitlab_rails['external_diffs_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
+ }
+ ```
+
+ Note that, if you are using AWS IAM profiles, be sure to omit the
+ AWS access key and secret access key/value pairs. For example:
+
+ ```ruby
+ gitlab_rails['external_diffs_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'use_iam_profile' => true
+ }
+ ```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
@@ -131,22 +131,22 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- external_diffs:
- enabled: true
- object_store:
- enabled: true
- remote_directory: "external-diffs" # The bucket name
- connection:
- provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACCESS_KEY_ID
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- region: eu-central-1
- ```
+ ```yaml
+ external_diffs:
+ enabled: true
+ object_store:
+ enabled: true
+ remote_directory: "external-diffs" # The bucket name
+ connection:
+ provider: AWS # Only AWS supported at the moment
+ aws_access_key_id: AWS_ACCESS_KEY_ID
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ region: eu-central-1
+ ```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
-### Alternative in-database storage
+## Alternative in-database storage
Enabling external diffs may reduce the performance of merge requests, as they
must be retrieved in a separate operation to other data. A compromise may be
@@ -157,11 +157,11 @@ To enable this feature, perform the following steps:
**In Omnibus installations:**
-1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['external_diffs_when'] = 'outdated'
- ```
+ ```ruby
+ gitlab_rails['external_diffs_when'] = 'outdated'
+ ```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
@@ -170,11 +170,11 @@ To enable this feature, perform the following steps:
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- external_diffs:
- enabled: true
- when: outdated
- ```
+ ```yaml
+ external_diffs:
+ enabled: true
+ when: outdated
+ ```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index 187fb2f73a1..4dd0bbbe937 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -1,6 +1,6 @@
# Grafana Configuration
-[Grafana](http://grafana.org/) is a tool that allows you to visualize time
+[Grafana](https://grafana.org/) is a tool that allows you to visualize time
series metrics through graphs and dashboards. It supports several backend
data stores, including InfluxDB. GitLab writes performance data to InfluxDB
and Grafana will allow you to query to display useful graphs.
@@ -13,7 +13,7 @@ services.
[GitLab Omnibus can help you install Grafana (recommended)](https://docs.gitlab.com/omnibus/settings/grafana.html)
or Grafana supplies package repositories (Yum/Apt) for easy installation.
-See [Grafana installation documentation](http://docs.grafana.org/installation/)
+See [Grafana installation documentation](https://grafana.com/docs/installation/)
for detailed steps.
NOTE: **Note:**
@@ -103,6 +103,21 @@ repository for more information on this process.
[grafana-dashboards]: https://gitlab.com/gitlab-org/grafana-dashboards
+## Integration with GitLab UI
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/61005) in GitLab 12.1.
+
+If you have set up Grafana, you can enable a link to access it easily from the sidebar:
+
+1. Go to the admin area under **Settings > Metrics and profiling**
+ and expand "Metrics - Grafana".
+1. Check the "Enable access to Grafana" checkbox.
+1. If Grafana is enabled through Omnibus GitLab and on the same server,
+ leave "Grafana URL" unchanged. In any other case, enter the full URL
+ path of the Grafana instance.
+1. Click **Save changes**.
+1. The new link will be available in the admin area under **Monitoring > Metrics Dashboard**.
+
---
Read more on:
diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md
index fa281f47ed8..cf6728510fe 100644
--- a/doc/administration/monitoring/performance/influxdb_configuration.md
+++ b/doc/administration/monitoring/performance/influxdb_configuration.md
@@ -187,7 +187,7 @@ Read more on:
[influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/
[influxdb cli]: https://docs.influxdata.com/influxdb/v0.9/tools/shell/
[udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
-[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[influxdb]: https://www.influxdata.com/products/influxdb-overview/
[tsm tree]: https://influxdata.com/blog/new-storage-engine-time-structured-merge-tree/
[tsm1-commit]: https://github.com/influxdata/influxdb/commit/15d723dc77651bac83e09e2b1c94be480966cb0d
[influx-admin]: https://docs.influxdata.com/influxdb/v0.9/administration/authentication_and_authorization/#create-a-new-admin-user
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 2d9e3f7f18b..89501f20d99 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -49,7 +49,7 @@ The following metrics are available:
| unicorn_queued_connections | Gauge | 11.0 | The number of queued Unicorn connections |
| unicorn_workers | Gauge | 12.0 | The number of Unicorn workers |
-## Sidekiq Metrics available for Geo **[PREMIUM]**
+## Sidekiq Metrics available for Geo **(PREMIUM)**
Sidekiq jobs may also gather metrics, and these metrics can be accessed if the Sidekiq exporter is enabled (e.g. via
the `monitoring.sidekiq_exporter` configuration option in `gitlab.yml`.
@@ -104,7 +104,7 @@ Some basic Ruby runtime metrics are available:
| ruby_process_cpu_seconds_total | Gauge | 12.0 | Total amount of CPU time per process |
| ruby_process_max_fds | Gauge | 12.0 | Maximum number of open file descriptors per process |
| ruby_process_resident_memory_bytes | Gauge | 12.0 | Memory usage by process, measured in bytes |
-| ruby_process_start_time_seconds | Gauge | 12.0 | The elapsed time between system boot and the process started, measured in seconds |
+| ruby_process_start_time_seconds | Gauge | 12.0 | UNIX timestamp of process start time |
[GC.stat]: https://ruby-doc.org/core-2.3.0/GC.html#method-c-stat
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
index 7297507f599..a16cd5166b7 100644
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ b/doc/administration/operations/extra_sidekiq_processes.md
@@ -1,4 +1,4 @@
-# Extra Sidekiq processes **[STARTER ONLY]**
+# Extra Sidekiq processes **(STARTER ONLY)**
NOTE: **Note:**
The information in this page applies only to Omnibus GitLab.
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index 3631ea0822f..b329abdca08 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -6,7 +6,7 @@ using [ssh certificates](ssh_certificates.md), they are even faster,
but are not a drop-in replacement.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
-> [GitLab Starter](https://about.gitlab.com/gitlab-ee) 9.3.
+> [GitLab Starter](https://about.gitlab.com/pricing/) 9.3.
>
> [Available in](https://gitlab.com/gitlab-org/gitlab-ee/issues/3953) GitLab
> Community Edition 10.4.
@@ -30,7 +30,7 @@ instructions will break installations using older versions of OpenSSH, such as
those included with CentOS 6 as of September 2017. If you want to use this
feature for CentOS 6, follow [the instructions on how to build and install a custom OpenSSH package](#compiling-a-custom-version-of-openssh-for-centos-6) before continuing.
-## Fast lookup is required for Geo **[PREMIUM]**
+## Fast lookup is required for Geo **(PREMIUM)**
By default, GitLab manages an `authorized_keys` file, which contains all the
public SSH keys for users allowed to access GitLab. However, to maintain a
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index df795a48169..df208b3f427 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -11,7 +11,7 @@ Keep your GitLab instance up and running smoothly.
by GitLab to another file system or another server.
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
to restart Sidekiq.
-- [Extra Sidekiq operations](extra_sidekiq_processes.md): Configure an extra set of Sidekiq processes to ensure certain queues always have dedicated workers, no matter the amount of jobs that need to be processed. **[STARTER ONLY]**
+- [Extra Sidekiq operations](extra_sidekiq_processes.md): Configure an extra set of Sidekiq processes to ensure certain queues always have dedicated workers, no matter the amount of jobs that need to be processed. **(STARTER ONLY)**
- [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer.
- Speed up SSH operations by [Authorizing SSH users via a fast,
indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md
index 0e2079cb093..ae67d7f08d6 100644
--- a/doc/administration/operations/unicorn.md
+++ b/doc/administration/operations/unicorn.md
@@ -2,7 +2,7 @@
## Unicorn
-GitLab uses [Unicorn](http://unicorn.bogomips.org/), a pre-forking Ruby web
+GitLab uses [Unicorn](https://bogomips.org/unicorn/), a pre-forking Ruby web
server, to handle web requests (web browsers and Git HTTP clients). Unicorn is
a daemon written in Ruby and C that can load and run a Ruby on Rails
application; in our case the Rails application is GitLab Community Edition or
diff --git a/doc/administration/packages.md b/doc/administration/packages.md
index 0d5f784b71e..c0f8777a8c0 100644
--- a/doc/administration/packages.md
+++ b/doc/administration/packages.md
@@ -1,4 +1,4 @@
-# GitLab Packages administration **[PREMIUM ONLY]**
+# GitLab Packages administration **(PREMIUM ONLY)**
GitLab Packages allows organizations to utilize GitLab as a private repository
for a variety of common package managers. Users are able to build and publish
@@ -11,7 +11,7 @@ The Packages feature allows GitLab to act as a repository for the following:
| [Maven Repository](../user/project/packages/maven_repository.md) | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ |
| [NPM Registry](../user/project/packages/npm_registry.md) | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ |
-Don't you see your package management system supported yet?
+Don't you see your package management system supported yet?
Please consider contributing
to GitLab. This [development documentation](../development/packages.md) will guide you through the process.
@@ -28,9 +28,9 @@ To enable the Packages feature:
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['packages_enabled'] = true
- ```
+ ```ruby
+ gitlab_rails['packages_enabled'] = true
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -39,10 +39,11 @@ To enable the Packages feature:
1. After the installation is complete, you will have to configure the `packages`
section in `config/gitlab.yml`. Set to `true` to enable it:
- ```yaml
- packages:
- enabled: true
- ```
+ ```yaml
+ packages:
+ enabled: true
+ ```
+
1. [Restart GitLab] for the changes to take effect.
## Changing the storage path
@@ -61,9 +62,9 @@ To change the local storage path:
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['packages_storage_path'] = "/mnt/packages"
- ```
+ ```ruby
+ gitlab_rails['packages_storage_path'] = "/mnt/packages"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -71,11 +72,12 @@ To change the local storage path:
1. Edit the `packages` section in `config/gitlab.yml`:
- ```yaml
- packages:
- enabled: true
- storage_path: shared/packages
- ```
+ ```yaml
+ packages:
+ enabled: true
+ storage_path: shared/packages
+ ```
+
1. [Restart GitLab] for the changes to take effect.
### Using object storage
@@ -88,31 +90,31 @@ upload packages:
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines (uncomment where
necessary):
- ```ruby
- gitlab_rails['packages_enabled'] = true
- gitlab_rails['packages_storage_path'] = "/var/opt/gitlab/gitlab-rails/shared/packages"
- gitlab_rails['packages_object_store_enabled'] = true
- gitlab_rails['packages_object_store_remote_directory'] = "packages" # The bucket name.
- gitlab_rails['packages_object_store_direct_upload'] = false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false).
- gitlab_rails['packages_object_store_background_upload'] = true # Temporary option to limit automatic upload (Default: true).
- gitlab_rails['packages_object_store_proxy_download'] = false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
- gitlab_rails['packages_object_store_connection'] = {
- ##
- ## If the provider is AWS S3, uncomment the following
- ##
- #'provider' => 'AWS',
- #'region' => 'eu-west-1',
- #'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- #'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY',
- ##
- ## If the provider is other than AWS (an S3-compatible one), uncomment the following
- ##
- #'host' => 's3.amazonaws.com',
- #'aws_signature_version' => 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
- #'endpoint' => 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
- #'path_style' => false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
- }
- ```
+ ```ruby
+ gitlab_rails['packages_enabled'] = true
+ gitlab_rails['packages_storage_path'] = "/var/opt/gitlab/gitlab-rails/shared/packages"
+ gitlab_rails['packages_object_store_enabled'] = true
+ gitlab_rails['packages_object_store_remote_directory'] = "packages" # The bucket name.
+ gitlab_rails['packages_object_store_direct_upload'] = false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false).
+ gitlab_rails['packages_object_store_background_upload'] = true # Temporary option to limit automatic upload (Default: true).
+ gitlab_rails['packages_object_store_proxy_download'] = false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
+ gitlab_rails['packages_object_store_connection'] = {
+ ##
+ ## If the provider is AWS S3, uncomment the following
+ ##
+ #'provider' => 'AWS',
+ #'region' => 'eu-west-1',
+ #'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ #'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY',
+ ##
+ ## If the provider is other than AWS (an S3-compatible one), uncomment the following
+ ##
+ #'host' => 's3.amazonaws.com',
+ #'aws_signature_version' => 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
+ #'endpoint' => 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
+ #'path_style' => false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
+ }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -120,35 +122,35 @@ upload packages:
1. Edit the `packages` section in `config/gitlab.yml` (uncomment where necessary):
- ```yaml
- packages:
- enabled: true
- ##
- ## The location where build packages are stored (default: shared/packages).
- ##
- #storage_path: shared/packages
- object_store:
- enabled: false
- remote_directory: packages # The bucket name.
- #direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false).
- #background_upload: true # Temporary option to limit automatic upload (Default: true).
- #proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
- connection:
- ##
- ## If the provider is AWS S3, uncomment the following
- ##
- #provider: AWS
- #region: us-east-1
- #aws_access_key_id: AWS_ACCESS_KEY_ID
- #aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- ##
- ## If the provider is other than AWS (an S3-compatible one), uncomment the following
- ##
- #host: 's3.amazonaws.com' # default: s3.amazonaws.com.
- #aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
- #endpoint: 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
- #path_style: false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
- ```
+ ```yaml
+ packages:
+ enabled: true
+ ##
+ ## The location where build packages are stored (default: shared/packages).
+ ##
+ #storage_path: shared/packages
+ object_store:
+ enabled: false
+ remote_directory: packages # The bucket name.
+ #direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false).
+ #background_upload: true # Temporary option to limit automatic upload (Default: true).
+ #proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage.
+ connection:
+ ##
+ ## If the provider is AWS S3, uncomment the following
+ ##
+ #provider: AWS
+ #region: us-east-1
+ #aws_access_key_id: AWS_ACCESS_KEY_ID
+ #aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ ##
+ ## If the provider is other than AWS (an S3-compatible one), uncomment the following
+ ##
+ #host: 's3.amazonaws.com' # default: s3.amazonaws.com.
+ #aws_signature_version: 4 # For creation of signed URLs. Set to 2 if provider does not support v4.
+ #endpoint: 'https://s3.amazonaws.com' # Useful for S3-compliant services such as DigitalOcean Spaces.
+ #path_style: false # If true, use 'host/bucket_name/object' instead of 'bucket_name.host/object'.
+ ```
1. [Restart GitLab] for the changes to take effect.
diff --git a/doc/administration/pseudonymizer.md b/doc/administration/pseudonymizer.md
index 036e1d3fe61..78b2751da13 100644
--- a/doc/administration/pseudonymizer.md
+++ b/doc/administration/pseudonymizer.md
@@ -1,4 +1,4 @@
-# Pseudonymizer **[ULTIMATE]**
+# Pseudonymizer **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5532) in [GitLab Ultimate][ee] 11.1.
@@ -22,36 +22,36 @@ To configure the pseudonymizer, you need to:
- Provide a manifest file that describes which fields should be included or
pseudonymized ([example `manifest.yml` file](https://gitlab.com/gitlab-org/gitlab-ee/tree/master/config/pseudonymizer.yml)).
- A default manifest is provided with the GitLab installation. Using a relative file path will be resolved from the Rails root.
+ A default manifest is provided with the GitLab installation. Using a relative file path will be resolved from the Rails root.
Alternatively, you can use an absolute file path.
-- Use an object storage and specify the connection parameters in the `pseudonymizer.upload.connection` configuration option.
+- Use an object storage and specify the connection parameters in the `pseudonymizer.upload.connection` configuration option.
**For Omnibus installations:**
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
the values you want:
- ```ruby
- gitlab_rails['pseudonymizer_manifest'] = 'config/pseudonymizer.yml'
- gitlab_rails['pseudonymizer_upload_remote_directory'] = 'gitlab-elt' # bucket name
- gitlab_rails['pseudonymizer_upload_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
- }
- ```
-
- NOTE: **Note:**
- If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
-
- ```ruby
- gitlab_rails['pseudonymizer_upload_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'use_iam_profile' => true
- }
- ```
+ ```ruby
+ gitlab_rails['pseudonymizer_manifest'] = 'config/pseudonymizer.yml'
+ gitlab_rails['pseudonymizer_upload_remote_directory'] = 'gitlab-elt' # bucket name
+ gitlab_rails['pseudonymizer_upload_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
+ }
+ ```
+
+ NOTE: **Note:**
+ If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
+
+ ```ruby
+ gitlab_rails['pseudonymizer_upload_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'use_iam_profile' => true
+ }
+ ```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect.
@@ -63,17 +63,17 @@ To configure the pseudonymizer, you need to:
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- pseudonymizer:
- manifest: config/pseudonymizer.yml
- upload:
- remote_directory: 'gitlab-elt' # bucket name
- connection:
- provider: AWS
- aws_access_key_id: AWS_ACCESS_KEY_ID
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- region: eu-central-1
- ```
+ ```yaml
+ pseudonymizer:
+ manifest: config/pseudonymizer.yml
+ upload:
+ remote_directory: 'gitlab-elt' # bucket name
+ connection:
+ provider: AWS
+ aws_access_key_id: AWS_ACCESS_KEY_ID
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ region: eu-central-1
+ ```
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source)
for the changes to take effect.
diff --git a/doc/administration/raketasks/geo.md b/doc/administration/raketasks/geo.md
index 9f3b31442f3..435aed8c413 100644
--- a/doc/administration/raketasks/geo.md
+++ b/doc/administration/raketasks/geo.md
@@ -1,4 +1,4 @@
-# Geo Rake Tasks **[PREMIUM ONLY]**
+# Geo Rake Tasks **(PREMIUM ONLY)**
## Git housekeeping
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index becd480a08f..2b31233d429 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -61,7 +61,7 @@ It will check that each component was set up according to the installation guide
You may also have a look at our Troubleshooting Guides:
-- [Troubleshooting Guide (GitLab)](http://docs.gitlab.com/ee/README.html#troubleshooting)
+- [Troubleshooting Guide (GitLab)](../index.md#troubleshooting)
- [Troubleshooting Guide (Omnibus Gitlab)](https://docs.gitlab.com/omnibus/README.html#troubleshooting)
**Omnibus Installation**
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index 6ca23aabdec..0599e12b913 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -1,4 +1,4 @@
-# Project import/export administration **[CORE ONLY]**
+# Project import/export administration **(CORE ONLY)**
>**Note:**
>
diff --git a/doc/administration/reply_by_email_postfix_setup.md b/doc/administration/reply_by_email_postfix_setup.md
index d57fc67c83e..406f7e8a034 100644
--- a/doc/administration/reply_by_email_postfix_setup.md
+++ b/doc/administration/reply_by_email_postfix_setup.md
@@ -14,109 +14,109 @@ The instructions make the assumption that you will be using the email address `i
1. Install the `postfix` package if it is not installed already:
- ```sh
- sudo apt-get install postfix
- ```
+ ```sh
+ sudo apt-get install postfix
+ ```
- When asked about the environment, select 'Internet Site'. When asked to confirm the hostname, make sure it matches `gitlab.example.com`.
+ When asked about the environment, select 'Internet Site'. When asked to confirm the hostname, make sure it matches gitlab.example.com`.
1. Install the `mailutils` package.
- ```sh
- sudo apt-get install mailutils
- ```
+ ```sh
+ sudo apt-get install mailutils
+ ```
## Create user
1. Create a user for incoming email.
- ```sh
- sudo useradd -m -s /bin/bash incoming
- ```
+ ```sh
+ sudo useradd -m -s /bin/bash incoming
+ ```
1. Set a password for this user.
- ```sh
- sudo passwd incoming
- ```
+ ```sh
+ sudo passwd incoming
+ ```
- Be sure not to forget this, you'll need it later.
+ Be sure not to forget this, you'll need it later.
## Test the out-of-the-box setup
1. Connect to the local SMTP server:
- ```sh
- telnet localhost 25
- ```
+ ```sh
+ telnet localhost 25
+ ```
- You should see a prompt like this:
+ You should see a prompt like this:
- ```sh
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
- 220 gitlab.example.com ESMTP Postfix (Ubuntu)
- ```
+ ```sh
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ 220 gitlab.example.com ESMTP Postfix (Ubuntu)
+ ```
- If you get a `Connection refused` error instead, verify that `postfix` is running:
+ If you get a `Connection refused` error instead, verify that `postfix` is running:
- ```sh
- sudo postfix status
- ```
+ ```sh
+ sudo postfix status
+ ```
- If it is not, start it:
+ If it is not, start it:
- ```sh
- sudo postfix start
- ```
+ ```sh
+ sudo postfix start
+ ```
1. Send the new `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
- ```
- ehlo localhost
- mail from: root@localhost
- rcpt to: incoming@localhost
- data
- Subject: Re: Some issue
+ ```
+ ehlo localhost
+ mail from: root@localhost
+ rcpt to: incoming@localhost
+ data
+ Subject: Re: Some issue
- Sounds good!
- .
- quit
- ```
+ Sounds good!
+ .
+ quit
+ ```
- _**Note:** The `.` is a literal period on its own line._
+ _**Note:** The `.` is a literal period on its own line._
- _**Note:** If you receive an error after entering `rcpt to: incoming@localhost`
- then your Postfix `my_network` configuration is not correct. The error will
- say 'Temporary lookup failure'. See
- [Configure Postfix to receive email from the Internet](#configure-postfix-to-receive-email-from-the-internet)._
+ _**Note:** If you receive an error after entering `rcpt to: incoming@localhost`
+ then your Postfix `my_network` configuration is not correct. The error will
+ say 'Temporary lookup failure'. See
+ [Configure Postfix to receive email from the Internet](#configure-postfix-to-receive-email-from-the-internet)._
1. Check if the `incoming` user received the email:
- ```sh
- su - incoming
- mail
- ```
+ ```sh
+ su - incoming
+ mail
+ ```
- You should see output like this:
+ You should see output like this:
- ```
- "/var/mail/incoming": 1 message 1 unread
- >U 1 root@localhost 59/2842 Re: Some issue
- ```
+ ```
+ "/var/mail/incoming": 1 message 1 unread
+ >U 1 root@localhost 59/2842 Re: Some issue
+ ```
- Quit the mail app:
+ Quit the mail app:
- ```sh
- q
- ```
+ ```sh
+ q
+ ```
1. Log out of the `incoming` account and go back to being `root`:
- ```sh
- logout
- ```
+ ```sh
+ logout
+ ```
## Configure Postfix to use Maildir-style mailboxes
@@ -124,208 +124,212 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
1. Configure Postfix to use Maildir-style mailboxes:
- ```sh
- sudo postconf -e "home_mailbox = Maildir/"
- ```
+ ```sh
+ sudo postconf -e "home_mailbox = Maildir/"
+ ```
1. Restart Postfix:
- ```sh
- sudo /etc/init.d/postfix restart
- ```
+ ```sh
+ sudo /etc/init.d/postfix restart
+ ```
1. Test the new setup:
- 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_.
- 1. Check if the `incoming` user received the email:
+ 1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_.
+ 1. Check if the `incoming` user received the email:
- ```sh
- su - incoming
- MAIL=/home/incoming/Maildir
- mail
- ```
+ ```sh
+ su - incoming
+ MAIL=/home/incoming/Maildir
+ mail
+ ```
- You should see output like this:
+ You should see output like this:
- ```
- "/home/incoming/Maildir": 1 message 1 unread
- >U 1 root@localhost 59/2842 Re: Some issue
- ```
+ ```
+ "/home/incoming/Maildir": 1 message 1 unread
+ >U 1 root@localhost 59/2842 Re: Some issue
+ ```
- Quit the mail app:
+ Quit the mail app:
- ```sh
- q
- ```
+ ```sh
+ q
+ ```
- _**Note:** If `mail` returns an error `Maildir: Is a directory` then your
- version of `mail` doesn't support Maildir style mailboxes. Install
- `heirloom-mailx` by running `sudo apt-get install heirloom-mailx`. Then,
- try the above steps again, substituting `heirloom-mailx` for the `mail`
- command._
+ _**Note:** If `mail` returns an error `Maildir: Is a directory` then your
+ version of `mail` doesn't support Maildir style mailboxes. Install
+ `heirloom-mailx` by running `sudo apt-get install heirloom-mailx`. Then,
+ try the above steps again, substituting `heirloom-mailx` for the `mail`
+ command._
1. Log out of the `incoming` account and go back to being `root`:
- ```sh
- logout
- ```
+ ```sh
+ logout
+ ```
## Install the Courier IMAP server
1. Install the `courier-imap` package:
- ```sh
- sudo apt-get install courier-imap
- ```
+ ```sh
+ sudo apt-get install courier-imap
+ ```
- And start `imapd`:
- ```sh
- imapd start
- ```
+ And start `imapd`:
+
+ ```sh
+ imapd start
+ ```
1. The courier-authdaemon isn't started after installation. Without it, imap authentication will fail:
- ```sh
- sudo service courier-authdaemon start
- ```
- You can also configure courier-authdaemon to start on boot:
- ```sh
- sudo systemctl enable courier-authdaemon
- ```
+
+ ```sh
+ sudo service courier-authdaemon start
+ ```
+
+ You can also configure courier-authdaemon to start on boot:
+
+ ```sh
+ sudo systemctl enable courier-authdaemon
+ ```
## Configure Postfix to receive email from the internet
1. Let Postfix know about the domains that it should consider local:
- ```sh
- sudo postconf -e "mydestination = gitlab.example.com, localhost.localdomain, localhost"
- ```
+ ```sh
+ sudo postconf -e "mydestination = gitlab.example.com, localhost.localdomain, localhost"
+ ```
1. Let Postfix know about the IPs that it should consider part of the LAN:
- We'll assume `192.168.1.0/24` is your local LAN. You can safely skip this step if you don't have other machines in the same local network.
+ We'll assume `192.168.1.0/24` is your local LAN. You can safely skip this step if you don't have other machines in the same local network.
- ```sh
- sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.1.0/24"
- ```
+ ```sh
+ sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.1.0/24"
+ ```
1. Configure Postfix to receive mail on all interfaces, which includes the internet:
- ```sh
- sudo postconf -e "inet_interfaces = all"
- ```
+ ```sh
+ sudo postconf -e "inet_interfaces = all"
+ ```
1. Configure Postfix to use the `+` delimiter for sub-addressing:
- ```sh
- sudo postconf -e "recipient_delimiter = +"
- ```
+ ```sh
+ sudo postconf -e "recipient_delimiter = +"
+ ```
1. Restart Postfix:
- ```sh
- sudo service postfix restart
- ```
+ ```sh
+ sudo service postfix restart
+ ```
## Test the final setup
1. Test SMTP under the new setup:
- 1. Connect to the SMTP server:
+ 1. Connect to the SMTP server:
- ```sh
- telnet gitlab.example.com 25
- ```
+ ```sh
+ telnet gitlab.example.com 25
+ ```
- You should see a prompt like this:
+ You should see a prompt like this:
- ```sh
- Trying 123.123.123.123...
- Connected to gitlab.example.com.
- Escape character is '^]'.
- 220 gitlab.example.com ESMTP Postfix (Ubuntu)
- ```
+ ```sh
+ Trying 123.123.123.123...
+ Connected to gitlab.example.com.
+ Escape character is '^]'.
+ 220 gitlab.example.com ESMTP Postfix (Ubuntu)
+ ```
- If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25.
+ If you get a `Connection refused` error instead, make sure your firewall is set up to allow inbound traffic on port 25.
- 1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
+ 1. Send the `incoming` user a dummy email to test SMTP, by entering the following into the SMTP prompt:
- ```
- ehlo gitlab.example.com
- mail from: root@gitlab.example.com
- rcpt to: incoming@gitlab.example.com
- data
- Subject: Re: Some issue
+ ```
+ ehlo gitlab.example.com
+ mail from: root@gitlab.example.com
+ rcpt to: incoming@gitlab.example.com
+ data
+ Subject: Re: Some issue
- Sounds good!
- .
- quit
- ```
+ Sounds good!
+ .
+ quit
+ ```
- (Note: The `.` is a literal period on its own line)
+ (Note: The `.` is a literal period on its own line)
- 1. Check if the `incoming` user received the email:
+ 1. Check if the `incoming` user received the email:
- ```sh
- su - incoming
- MAIL=/home/incoming/Maildir
- mail
- ```
+ ```sh
+ su - incoming
+ MAIL=/home/incoming/Maildir
+ mail
+ ```
- You should see output like this:
+ You should see output like this:
- ```
- "/home/incoming/Maildir": 1 message 1 unread
- >U 1 root@gitlab.example.com 59/2842 Re: Some issue
- ```
+ ```
+ "/home/incoming/Maildir": 1 message 1 unread
+ >U 1 root@gitlab.example.com 59/2842 Re: Some issue
+ ```
- Quit the mail app:
+ Quit the mail app:
- ```sh
- q
- ```
+ ```sh
+ q
+ ```
- 1. Log out of the `incoming` account and go back to being `root`:
+ 1. Log out of the `incoming` account and go back to being `root`:
- ```sh
- logout
- ```
+ ```sh
+ logout
+ ```
1. Test IMAP under the new setup:
- 1. Connect to the IMAP server:
+ 1. Connect to the IMAP server:
- ```sh
- telnet gitlab.example.com 143
- ```
+ ```sh
+ telnet gitlab.example.com 143
+ ```
- You should see a prompt like this:
+ You should see a prompt like this:
- ```sh
- Trying 123.123.123.123...
- Connected to mail.example.gitlab.com.
- Escape character is '^]'.
- - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information.
- ```
+ ```sh
+ Trying 123.123.123.123...
+ Connected to mail.example.gitlab.com.
+ Escape character is '^]'.
+ - OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc. See COPYING for distribution information.
+ ```
- 1. Sign in as the `incoming` user to test IMAP, by entering the following into the IMAP prompt:
+ 1. Sign in as the `incoming` user to test IMAP, by entering the following into the IMAP prompt:
- ```
- a login incoming PASSWORD
- ```
+ ```
+ a login incoming PASSWORD
+ ```
- Replace PASSWORD with the password you set on the `incoming` user earlier.
+ Replace PASSWORD with the password you set on the `incoming` user earlier.
- You should see output like this:
+ You should see output like this:
- ```
- a OK LOGIN Ok.
- ```
+ ```
+ a OK LOGIN Ok.
+ ```
- 1. Disconnect from the IMAP server:
+ 1. Disconnect from the IMAP server:
- ```sh
- a logout
- ```
+ ```sh
+ a logout
+ ```
## Done!
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index 4aafc06cfdc..3de860f9240 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -68,18 +68,18 @@ NOTE: **Note:** This example uses NFS and CephFS. We do not recommend using EFS
1. Edit `gitlab.yml` and add the storage paths:
- ```yaml
- repositories:
- # Paths where repositories can be stored. Give the canonicalized absolute pathname.
- # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!!
- storages: # You must have at least a 'default' storage path.
- default:
- path: /home/git/repositories
- nfs:
- path: /mnt/nfs/repositories
- cephfs:
- path: /mnt/cephfs/repositories
- ```
+ ```yaml
+ repositories:
+ # Paths where repositories can be stored. Give the canonicalized absolute pathname.
+ # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!!
+ storages: # You must have at least a 'default' storage path.
+ default:
+ path: /home/git/repositories
+ nfs:
+ path: /mnt/nfs/repositories
+ cephfs:
+ path: /mnt/cephfs/repositories
+ ```
1. [Restart GitLab][restart-gitlab] for the changes to take effect.
@@ -97,16 +97,16 @@ working, you can remove the `repos_path` line.
1. Edit `/etc/gitlab/gitlab.rb` by appending the rest of the paths to the
default one:
- ```ruby
- git_data_dirs({
- "default" => { "path" => "/var/opt/gitlab/git-data" },
- "nfs" => { "path" => "/mnt/nfs/git-data" },
- "cephfs" => { "path" => "/mnt/cephfs/git-data" }
- })
- ```
+ ```ruby
+ git_data_dirs({
+ "default" => { "path" => "/var/opt/gitlab/git-data" },
+ "nfs" => { "path" => "/mnt/nfs/git-data" },
+ "cephfs" => { "path" => "/mnt/cephfs/git-data" }
+ })
+ ```
- Note that Omnibus stores the repositories in a `repositories` subdirectory
- of the `git-data` directory.
+ Note that Omnibus stores the repositories in a `repositories` subdirectory
+ of the `git-data` directory.
## Choose where new project repositories will be stored
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 834b41b3a2c..9dea6074a3f 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -6,19 +6,19 @@ Two different storage layouts can be used
to store the repositories on disk and their characteristics.
GitLab can be configured to use one or multiple repository shard locations
-that can be:
+that can be:
- Mounted to the local disk
- Exposed as an NFS shared volume
- Acessed via [gitaly] on its own machine.
In GitLab, this is configured in `/etc/gitlab/gitlab.rb` by the `git_data_dirs({})`
-configuration hash. The storage layouts discussed here will apply to any shard
+configuration hash. The storage layouts discussed here will apply to any shard
defined in it.
The `default` repository shard that is available in any installations
that haven't customized it, points to the local folder: `/var/opt/gitlab/git-data`.
-Anything discussed below is expected to be part of that folder.
+Anything discussed below is expected to be part of that folder.
## Legacy Storage
@@ -80,25 +80,20 @@ by another folder with the next 2 characters. They are both stored in a special
### Hashed object pools
-CAUTION: **Beta:**
-Hashed objects pools are considered beta, and are not ready for production use.
-Follow [gitaly#1548](https://gitlab.com/gitlab-org/gitaly/issues/1548) for
-updates.
+> [Introduced](https://gitlab.com/gitlab-org/gitaly/issues/1606) in GitLab 12.1.
-For deduplication of public forks and their parent repository, objects are pooled
-in an object pool. These object pools are a third repository where shared objects
-are stored.
+Forks of public projects are deduplicated by creating a third repository, the object pool, containing the objects from the source project. Using `objects/info/alternates`, the source project and forks use the object pool for shared objects. Objects are moved from the source project to the object pool when housekeeping is run on the source project.
```ruby
# object pool paths
"@pools/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git"
```
-The object pool feature is behind the `object_pools` feature flag, and can be
-enabled for individual projects by executing
-`Feature.enable(:object_pools, Project.find(<id>))`. Note that the project has to
-be on hashed storage, should not be a fork itself, and hashed storage should be
-enabled for all new projects.
+Object pools can be disabled using the `object_pools` feature flag, and can be
+disabled for individual projects by executing
+`Feature.disable(:object_pools, Project.find(<id>))`. Disabling object pools
+will not change existing deduplicated forks, but will prevent new forks from
+being deduplicated.
DANGER: **Danger:**
Do not run `git prune` or `git gc` in pool repositories! This can
@@ -108,7 +103,7 @@ question.
### How to migrate to Hashed Storage
To start a migration, enable Hashed Storage for new projects:
-
+
1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section.
2. Select the **Use hashed storage paths for newly created and renamed projects** checkbox.
@@ -129,7 +124,7 @@ an Omnibus Gitlab installation:
sudo gitlab-rake gitlab:storage:migrate_to_hashed ID_FROM=50 ID_TO=100
```
-Check the [documentation][rake/migrate-to-hashed] for additional information and instructions for
+Check the [documentation][rake/migrate-to-hashed] for additional information and instructions for
source-based installation.
#### Rollback
@@ -140,12 +135,12 @@ projects:
1. Go to **Admin > Settings > Repository** and expand the **Repository Storage** section.
2. Uncheck the **Use hashed storage paths for newly created and renamed projects** checkbox.
-To schedule a complete rollback, see the
+To schedule a complete rollback, see the
[rake task documentation for storage rollback](raketasks/storage.md#rollback-from-hashed-storage-to-legacy-storage) for instructions.
The rollback task also supports specifying a range of Project IDs. Here is an example
of limiting the rollout to Project IDs 50 to 100, in an Omnibus Gitlab installation:
-
+
```bash
sudo gitlab-rake gitlab:storage:rollback_to_legacy ID_FROM=50 ID_TO=100
```
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
index cbc3fbd9473..e23f2052d04 100644
--- a/doc/administration/restart_gitlab.md
+++ b/doc/administration/restart_gitlab.md
@@ -137,9 +137,9 @@ If you are using other init systems, like systemd, you can check the
[GitLab Recipes][gl-recipes] repository for some unofficial services. These are
**not** officially supported so use them at your own risk.
-[omnibus-dl]: https://about.gitlab.com/downloads/ "Download the Omnibus packages"
+[omnibus-dl]: https://about.gitlab.com/install/ "Download the Omnibus packages"
[install]: ../install/installation.md "Documentation to install GitLab from source"
[mailroom]: reply_by_email.md "Used for replying by email in GitLab issues and merge requests"
-[chef]: https://www.chef.io/chef/ "Chef official website"
+[chef]: https://www.chef.io/products/chef-infra/ "Chef official website"
[src-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/support/init.d/gitlab "GitLab init service file"
[gl-recipes]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/init "GitLab Recipes repository"
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 708b59a273b..c6529812ec3 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -3,7 +3,7 @@
>**Notes:**
Uploads represent all user data that may be sent to GitLab as a single file. As an example, avatars and notes' attachments are uploads. Uploads are integral to GitLab functionality, and therefore cannot be disabled.
-### Using local storage
+## Using local storage
>**Notes:**
This is the default configuration
@@ -23,10 +23,10 @@ _The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._
1. To change the storage path for example to `/mnt/storage/uploads`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
- ```ruby
- gitlab_rails['uploads_storage_path'] = "/mnt/storage/"
- gitlab_rails['uploads_base_dir'] = "uploads"
- ```
+ ```ruby
+ gitlab_rails['uploads_storage_path'] = "/mnt/storage/"
+ gitlab_rails['uploads_base_dir'] = "uploads"
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
@@ -40,15 +40,15 @@ _The uploads are stored by default in
1. To change the storage path for example to `/mnt/storage/uploads`, edit
`/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
- ```yaml
- uploads:
- storage_path: /mnt/storage
- base_dir: uploads
- ```
+ ```yaml
+ uploads:
+ storage_path: /mnt/storage
+ base_dir: uploads
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
-### Using object storage **[CORE ONLY]**
+## Using object storage **(CORE ONLY)**
> **Notes:**
>
@@ -60,7 +60,7 @@ If you don't want to use the local disk where GitLab is installed to store the
uploads, you can use an object storage provider like AWS S3 instead.
This configuration relies on valid AWS credentials to be configured already.
-### Object Storage Settings
+## Object Storage Settings
For source installations the following settings are nested under `uploads:` and then `object_store:`. On omnibus installs they are prefixed by `uploads_object_store_`.
@@ -73,7 +73,7 @@ For source installations the following settings are nested under `uploads:` and
| `proxy_download` | Set to true to enable proxying all files served. Option allows to reduce egress traffic as this allows clients to download directly from remote storage instead of proxying all data | `false` |
| `connection` | Various connection options described below | |
-#### S3 compatible connection settings
+### S3 compatible connection settings
The connection settings match those provided by [Fog](https://github.com/fog), and are as follows:
@@ -97,27 +97,27 @@ _The uploads are stored by default in
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
the values you want:
- ```ruby
- gitlab_rails['uploads_object_store_enabled'] = true
- gitlab_rails['uploads_object_store_remote_directory'] = "uploads"
- gitlab_rails['uploads_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
- 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
- }
- ```
-
- >**Note:**
- If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
-
- ```ruby
- gitlab_rails['uploads_object_store_connection'] = {
- 'provider' => 'AWS',
- 'region' => 'eu-central-1',
- 'use_iam_profile' => true
- }
- ```
+ ```ruby
+ gitlab_rails['uploads_object_store_enabled'] = true
+ gitlab_rails['uploads_object_store_remote_directory'] = "uploads"
+ gitlab_rails['uploads_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
+ 'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
+ }
+ ```
+
+ >**Note:**
+ >If you are using AWS IAM profiles, be sure to omit the AWS access key and secret access key/value pairs.
+
+ ```ruby
+ gitlab_rails['uploads_object_store_connection'] = {
+ 'provider' => 'AWS',
+ 'region' => 'eu-central-1',
+ 'use_iam_profile' => true
+ }
+ ```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md).
@@ -132,17 +132,17 @@ _The uploads are stored by default in
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
- ```yaml
- uploads:
- object_store:
- enabled: true
- remote_directory: "uploads" # The bucket name
- connection:
- provider: AWS # Only AWS supported at the moment
- aws_access_key_id: AWS_ACESS_KEY_ID
- aws_secret_access_key: AWS_SECRET_ACCESS_KEY
- region: eu-central-1
- ```
+ ```yaml
+ uploads:
+ object_store:
+ enabled: true
+ remote_directory: "uploads" # The bucket name
+ connection:
+ provider: AWS # Only AWS supported at the moment
+ aws_access_key_id: AWS_ACESS_KEY_ID
+ aws_secret_access_key: AWS_SECRET_ACCESS_KEY
+ region: eu-central-1
+ ```
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md).
diff --git a/doc/api/README.md b/doc/api/README.md
index 23c69deef23..3ded230370c 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -37,16 +37,16 @@ The following API resources are available in the project context:
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
| [Issue boards](boards.md) | `/projects/:id/boards` |
-| [Issue links](issue_links.md) **[STARTER]** | `/projects/:id/issues/.../links` |
+| [Issue links](issue_links.md) **(STARTER)** | `/projects/:id/issues/.../links` |
| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
| [Labels](labels.md) | `/projects/:id/labels` |
-| [Managed licenses](managed_licenses.md) **[ULTIMATE]** | `/projects/:id/managed_licenses` |
+| [Managed licenses](managed_licenses.md) **(ULTIMATE)** | `/projects/:id/managed_licenses` |
| [Members](members.md) | `/projects/:id/members` (also available for groups) |
-| [Merge request approvals](merge_request_approvals.md) **[STARTER]** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` |
+| [Merge request approvals](merge_request_approvals.md) **(STARTER)** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` |
| [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) |
| [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` (also available for groups) |
| [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) |
-| [Packages](packages.md) **[PREMIUM]** | `/projects/:id/packages` |
+| [Packages](packages.md) **(PREMIUM)** | `/projects/:id/packages` |
| [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) |
| [Pipelines](pipelines.md) | `/projects/:id/pipelines` |
| [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` |
@@ -71,7 +71,7 @@ The following API resources are available in the project context:
| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
| [Services](services.md) | `/projects/:id/services` |
| [Tags](tags.md) | `/projects/:id/repository/tags` |
-| [Vulnerabilities](vulnerabilities.md) **[ULTIMATE]** | `/projects/:id/vulnerabilities` (also available for groups) |
+| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` (also available for groups) |
| [Wikis](wikis.md) | `/projects/:id/wikis` |
### Group resources
@@ -82,10 +82,10 @@ The following API resources are available in the group context:
|:-----------------------------------------------------------------|:---------------------------------------------------------------------------------|
| [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) |
| [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) |
-| [Discussions](discussions.md) (threaded comments) **[ULTIMATE]** | `/groups/:id/epics/.../discussions` (also available for projects) |
-| [Epic issues](epic_issues.md) **[ULTIMATE]** | `/groups/:id/epics/.../issues` |
-| [Epic links](epic_links.md) **[ULTIMATE]** | `/groups/:id/epics/.../epics` |
-| [Epics](epics.md) **[ULTIMATE]** | `/groups/:id/epics` |
+| [Discussions](discussions.md) (threaded comments) **(ULTIMATE)** | `/groups/:id/epics/.../discussions` (also available for projects) |
+| [Epic issues](epic_issues.md) **(ULTIMATE)** | `/groups/:id/epics/.../issues` |
+| [Epic links](epic_links.md) **(ULTIMATE)** | `/groups/:id/epics/.../epics` |
+| [Epics](epics.md) **(ULTIMATE)** | `/groups/:id/epics` |
| [Groups](groups.md) | `/groups`, `/groups/.../subgroups` |
| [Group badges](group_badges.md) | `/groups/:id/badges` |
| [Group issue boards](group_boards.md) | `/groups/:id/boards` |
@@ -115,12 +115,12 @@ The following API resources are available outside of project and group contexts
| [Deploy keys](deploy_keys.md) | `/deploy_keys` (also available for projects) |
| [Events](events.md) | `/events`, `/users/:id/events` (also available for projects) |
| [Feature flags](features.md) | `/features` |
-| [Geo Nodes](geo_nodes.md) **[PREMIUM ONLY]** | `/geo_nodes` |
+| [Geo Nodes](geo_nodes.md) **(PREMIUM ONLY)** | `/geo_nodes` |
| [Import repository from GitHub](import.md) | `/import/github` |
| [Issues](issues.md) | `/issues` (also available for groups and projects) |
| [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) |
| [Keys](keys.md) | `/keys` |
-| [License](license.md) **[CORE ONLY]** | `/license` |
+| [License](license.md) **(CORE ONLY)** | `/license` |
| [Markdown](markdown.md) | `/markdown` |
| [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) |
| [Namespaces](namespaces.md) | `/namespaces` |
@@ -147,7 +147,7 @@ Endpoints are available for:
- [GitLab CI YAML templates](templates/gitlab_ci_ymls.md).
- [Open source license templates](templates/licenses.md).
-## SCIM **[SILVER ONLY]**
+## SCIM **(SILVER ONLY)**
[GitLab.com Silver and above](https://about.gitlab.com/pricing/) provides an [SCIM API](scim.md) that implements [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644) and provides
the `/Users` endpoint. The base URL is: `/api/scim/v2/groups/:group_path/Users/`.
@@ -311,9 +311,9 @@ By default, impersonation is enabled. To disable impersonation:
1. Edit `/etc/gitlab/gitlab.rb`:
- ```ruby
- gitlab_rails['impersonation_enabled'] = false
- ```
+ ```ruby
+ gitlab_rails['impersonation_enabled'] = false
+ ```
1. Save the file and [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
GitLab for the changes to take effect.
@@ -326,10 +326,10 @@ To re-enable impersonation, remove this configuration and reconfigure GitLab.
1. Edit `config/gitlab.yml`:
- ```yaml
- gitlab:
- impersonation_enabled: false
- ```
+ ```yaml
+ gitlab:
+ impersonation_enabled: false
+ ```
1. Save the file and [restart](../administration/restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
diff --git a/doc/api/boards.md b/doc/api/boards.md
index a96206f5df3..08ec1d832df 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -141,7 +141,7 @@ Example response:
}
```
-## Create a board **[STARTER]**
+## Create a board **(STARTER)**
Creates a board.
@@ -209,7 +209,7 @@ Example response:
}
```
-## Update a board **[STARTER]**
+## Update a board **(STARTER)**
> [Introduced][ee-5954] in [GitLab Starter](https://about.gitlab.com/pricing/) 11.1.
@@ -229,7 +229,6 @@ PUT /projects/:id/boards/:board_id
| `labels` | string | no | Comma-separated list of label names which the board should be scoped to |
| `weight` | integer | no | The weight range from 0 to 9, to which the board should be scoped to |
-
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/boards/1?name=new_name&milestone_id=43&assignee_id=1&labels=Doing&weight=4
```
@@ -291,7 +290,7 @@ Example response:
}
```
-## Delete a board **[STARTER]**
+## Delete a board **(STARTER)**
Deletes a board.
@@ -405,8 +404,8 @@ POST /projects/:id/boards/:board_id/lists
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
| `label_id` | integer | no | The ID of a label |
-| `assignee_id` **[PREMIUM]** | integer | no | The ID of a user |
-| `milestone_id` **[PREMIUM]** | integer | no | The ID of a milestone |
+| `assignee_id` **(PREMIUM)** | integer | no | The ID of a user |
+| `milestone_id` **(PREMIUM)** | integer | no | The ID of a milestone |
NOTE: **Note**:
Label, assignee and milestone arguments are mutually exclusive,
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index 1d7523fcc3d..41f6ab436e8 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -19,13 +19,13 @@ Example response:
{
"id": 1,
"title": "Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T10:12:29Z"
},
{
"id": 3,
"title": "Another Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T11:12:29Z"
}
]
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index 9defef4fd53..208b8dca2e2 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -4,7 +4,7 @@ Discussions are a set of related notes on:
- Snippets
- Issues
-- Epics **[ULTIMATE]**
+- Epics **(ULTIMATE)**
- Merge requests
- Commits
@@ -430,7 +430,7 @@ Parameters:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions/636
```
-## Epics **[ULTIMATE]**
+## Epics **(ULTIMATE)**
### List group epic discussions
diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md
index ec59ea7068e..02317cc6c09 100644
--- a/doc/api/epic_issues.md
+++ b/doc/api/epic_issues.md
@@ -1,4 +1,4 @@
-# Epic Issues API **[ULTIMATE]**
+# Epic Issues API **(ULTIMATE)**
Every API call to epic_issues must be authenticated.
diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md
index 9ad90a6d0f1..6089198e46a 100644
--- a/doc/api/epic_links.md
+++ b/doc/api/epic_links.md
@@ -1,4 +1,4 @@
-# Epic Links API **[ULTIMATE]**
+# Epic Links API **(ULTIMATE)**
>**Note:**
> This endpoint was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9188) in GitLab 11.8.
diff --git a/doc/api/epics.md b/doc/api/epics.md
index 0541cfaa715..d05eb0a8804 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -1,4 +1,4 @@
-# Epics API **[ULTIMATE]**
+# Epics API **(ULTIMATE)**
Every API call to epic must be authenticated.
@@ -302,7 +302,7 @@ POST /groups/:id/epics/:epic_iid/todo
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `epic_iid ` | integer | yes | The internal ID of a group's epic |
+| `epic_iid` | integer | yes | The internal ID of a group's epic |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/1/epics/5/todo
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index ea31abdd87e..ac64cbedf7d 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -1,4 +1,4 @@
-# Geo Nodes API **[PREMIUM ONLY]**
+# Geo Nodes API **(PREMIUM ONLY)**
In order to interact with Geo node endpoints, you need to authenticate yourself
as an admin.
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index 6c1cce620ca..db073321808 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -1,4 +1,4 @@
-# GraphQL API (Alpha)
+# GraphQL API
> [Introduced][ce-19008] in GitLab 11.0.
@@ -23,30 +23,20 @@ programmatically with GitLab. To achieve this, it needs full coverage - anything
possible in the REST API should also be possible in the GraphQL API.
To help us meet this vision, the frontend should use GraphQL in preference to
-the REST API for new features, although the alpha status of GraphQL may prevent
-this from being a possibility at times.
+the REST API for new features.
There are no plans to deprecate the REST API. To reduce the technical burden of
supporting two APIs in parallel, they should share implementations as much as
possible.
-## Enabling the GraphQL feature
-
-The GraphQL API itself is currently in Alpha, and therefore hidden behind a
-feature flag. You can enable the feature using the [features api][features-api] on a self-hosted instance.
-
-For example:
-
-```shell
-curl --data "value=100" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/graphql
-```
+As of the 12.1 release, GraphQL is always enabled.
## Available queries
A first iteration of a GraphQL API includes the following queries
1. `project` : Within a project it is also possible to fetch a `mergeRequest` by IID.
-1. `group` : Basic group information and epics **[ULTIMATE]** are currently supported.
+1. `group` : Basic group information and epics **(ULTIMATE)** are currently supported.
1. `namespace` : Within a namespace it is also possible to fetch `projects`.
### Multiplex queries
diff --git a/doc/api/group_boards.md b/doc/api/group_boards.md
index 4bee05a128a..4d10f83720b 100644
--- a/doc/api/group_boards.md
+++ b/doc/api/group_boards.md
@@ -5,7 +5,7 @@ Every API call to group boards must be authenticated.
If a user is not a member of a group and the group is private, a `GET`
request will result in `404` status code.
-## Group Board
+## List all group issue boards in a group
Lists Issue Boards in the given group.
@@ -27,7 +27,16 @@ Example response:
[
{
"id": 1,
- "group_id": 5,
+ "name:": "group issue board",
+ "group": {
+ "id": 5,
+ "name": "Documentcloud",
+ "web_url": "http://example.com/groups/documentcloud"
+ },
+ "milestone": {
+ "id": 12
+ "title": "10.0"
+ },
"lists" : [
{
"id" : 1,
@@ -62,8 +71,7 @@ Example response:
```
Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will see
-different parameters, due to the ability to have multiple group boards. Refer to the table
-above to see what enpoint(s) belong to each tier.
+different parameters, due to the ability to have multiple group boards.
Example response:
@@ -114,9 +122,9 @@ Example response:
]
```
-## Single board
+## Single group issue board
-Gets a single board.
+Gets a single group issue board.
```
GET /groups/:id/boards/:board_id
@@ -136,7 +144,16 @@ Example response:
```json
{
"id": 1,
- "group_id": 5,
+ "name:": "group issue board",
+ "group": {
+ "id": 5,
+ "name": "Documentcloud",
+ "web_url": "http://example.com/groups/documentcloud"
+ },
+ "milestone": {
+ "id": 12
+ "title": "10.0"
+ },
"lists" : [
{
"id" : 1,
@@ -170,7 +187,7 @@ Example response:
```
Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will see
-different parameters, due to the ability to have multiple group boards:
+different parameters, due to the ability to have multiple group issue boards.s
Example response:
@@ -219,7 +236,7 @@ Example response:
}
```
-## Create a Group Issue Board **[PREMIUM]**
+## Create a group issue board **(PREMIUM)**
Creates a Group Issue Board.
@@ -283,9 +300,9 @@ Example response:
}
```
-## Update a Group Issue Board **[PREMIUM]**
+## Update a group issue board **(PREMIUM)**
-> [Introduced][ee-5954] in GitLab 11.1.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5954) in GitLab 11.1.
Updates a Group Issue Board.
@@ -303,7 +320,6 @@ PUT /groups/:id/boards/:board_id
| `labels` | string | no | Comma-separated list of label names which the board should be scoped to |
| `weight` | integer | no | The weight range from 0 to 9, to which the board should be scoped to |
-
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/boards/1?name=new_name&milestone_id=44&assignee_id=1&labels=GroupLabel&weight=4
```
@@ -352,7 +368,7 @@ Example response:
}
```
-## Delete a Group Issue Board **[PREMIUM]**
+## Delete a group issue board **(PREMIUM)**
Deletes a Group Issue Board.
@@ -369,7 +385,7 @@ DELETE /groups/:id/boards/:board_id
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/boards/1
```
-## List board lists
+## List group issue board lists
Get a list of the board's lists.
Does not include `open` and `closed` lists
@@ -421,7 +437,7 @@ Example response:
]
```
-## Single board list
+## Single group issue board list
Get a single board list.
@@ -453,7 +469,7 @@ Example response:
}
```
-## New board list
+## New group issue board list
Creates a new Issue Board list.
@@ -485,7 +501,7 @@ Example response:
}
```
-## Edit board list
+## Edit group issue board list
Updates an existing Issue Board list. This call is used to change list position.
@@ -518,7 +534,7 @@ Example response:
}
```
-## Delete a board list
+## Delete a group issue board list
Only for admins and group owners. Soft deletes the board list in question.
@@ -535,5 +551,3 @@ DELETE /groups/:id/boards/:board_id/lists/:list_id
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/boards/1/lists/1
```
-
-[ee-5954]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5954
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index e780a60a416..a819e06bcd9 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -18,13 +18,13 @@ GET /groups/:id/milestones?search=version
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `iids[]` | Array[integer] | optional | Return only the milestones having the given `iid` |
-| `state` | string | optional | Return only `active` or `closed` milestones |
-| `title` | string | optional | Return only the milestones having the given `title` |
-| `search` | string | optional | Return only milestones with a title or description matching the provided string |
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `iids[]` | integer array | optional | Return only the milestones having the given `iid` |
+| `state` | string | optional | Return only `active` or `closed` milestones |
+| `title` | string | optional | Return only the milestones having the given `title` |
+| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/milestones
@@ -137,7 +137,7 @@ Parameters:
[ce-12819]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12819
-## Get all burndown chart events for a single milestone **[STARTER]**
+## Get all burndown chart events for a single milestone **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4737) in GitLab 12.1
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 80a2fb8e4d9..d05e4b29fef 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -7,17 +7,17 @@ authentication, only public groups are returned.
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
-| `search` | string | no | Return the list of authorized groups matching the search criteria |
-| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
-| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
-| `statistics` | boolean | no | Include group statistics (admins only) |
-| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
-| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
+| Attribute | Type | Required | Description |
+| ------------------------ | ----------------- | -------- | ---------- |
+| `skip_groups` | array of integers | no | Skip the group IDs passed |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
+| `search` | string | no | Return the list of authorized groups matching the search criteria |
+| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
+| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
+| `statistics` | boolean | no | Include group statistics (admins only) |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups
@@ -94,18 +94,18 @@ When accessed without authentication, only public groups are returned.
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
-| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
-| `search` | string | no | Return the list of authorized groups matching the search criteria |
-| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
-| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
-| `statistics` | boolean | no | Include group statistics (admins only) |
-| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
-| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
+| Attribute | Type | Required | Description |
+| ------------------------ | ----------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
+| `skip_groups` | array of integers | no | Skip the group IDs passed |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have preceden |
+| `search` | string | no | Return the list of authorized groups matching the search criteria |
+| `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
+| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
+| `statistics` | boolean | no | Include group statistics (admins only) |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
```
GET /groups/:id/subgroups
@@ -142,22 +142,22 @@ GET /groups/:id/projects
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `archived` | boolean | no | Limit by archived status |
-| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
-| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
-| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
-| `search` | string | no | Return list of authorized projects matching the search criteria |
-| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
-| `owned` | boolean | no | Limit by projects owned by the current user |
-| `starred` | boolean | no | Limit by projects starred by the current user |
-| `with_issues_enabled` | boolean | no | Limit by projects with issues feature enabled. Default is `false` |
-| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
-| `with_shared` | boolean | no | Include projects shared to this group. Default is `true` |
-| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
-| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
+| Attribute | Type | Required | Description |
+| ----------------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `archived` | boolean | no | Limit by archived status |
+| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
+| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
+| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
+| `search` | string | no | Return list of authorized projects matching the search criteria |
+| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `owned` | boolean | no | Limit by projects owned by the current user |
+| `starred` | boolean | no | Limit by projects starred by the current user |
+| `with_issues_enabled` | boolean | no | Limit by projects with issues feature enabled. Default is `false` |
+| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
+| `with_shared` | boolean | no | Include projects shared to this group. Default is `true` |
+| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
Example response:
@@ -214,11 +214,11 @@ GET /groups/:id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `with_projects` | boolean | no | Include details from projects that belong to the specified group (defaults to `true`). |
+| Attribute | Type | Required | Description |
+| ------------------------ | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only). |
+| `with_projects` | boolean | no | Include details from projects that belong to the specified group (defaults to `true`). |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4
@@ -378,11 +378,16 @@ Example response:
Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see
the `shared_runners_minutes_limit` and `extra_shared_runners_minutes_limit` parameters:
-Additional response parameters: **[STARTER]**
+Additional response parameters:
```json
+{
+ "id": 4,
+ "description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
"shared_runners_minutes_limit": 133,
"extra_shared_runners_minutes_limit": 133,
+ ...
+}
```
When adding the parameter `with_projects=false`, projects will not be returned.
@@ -420,17 +425,17 @@ POST /groups
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `name` | string | yes | The name of the group |
-| `path` | string | yes | The path of the group |
-| `description` | string | no | The group's description |
-| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
-| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
-| `request_access_enabled` | boolean | no | Allow users to request member access. |
-| `parent_id` | integer | no | The parent group ID for creating nested group. |
-| `shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Pipeline minutes quota for this group. |
-| `extra_shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Extra pipeline minutes quota for this group. |
+| Attribute | Type | Required | Description |
+| ------------------------------------ | ------- | -------- | ----------- |
+| `name` | string | yes | The name of the group. |
+| `path` | string | yes | The path of the group. |
+| `description` | string | no | The group's description. |
+| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
+| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
+| `request_access_enabled` | boolean | no | Allow users to request member access. |
+| `parent_id` | integer | no | The parent group ID for creating nested group. |
+| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. |
+| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. |
## Transfer project to group
@@ -442,10 +447,10 @@ POST /groups/:id/projects/:project_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| Attribute | Type | Required | Description |
+| ------------ | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
## Update group
@@ -455,20 +460,20 @@ Updates the project group. Only available to group owners and administrators.
PUT /groups/:id
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the group |
-| `name` | string | no | The name of the group |
-| `path` | string | no | The path of the group |
-| `description` | string | no | The description of the group |
-| `membership_lock` | boolean | no | **[STARTER]** Prevent adding new members to project membership within this group |
-| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group |
-| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
-| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
-| `request_access_enabled` | boolean | no | Allow users to request member access. |
-| `file_template_project_id` | integer | no | **[PREMIUM]** The ID of a project to load custom file templates from |
-| `shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Pipeline minutes quota for this group |
-| `extra_shared_runners_minutes_limit` | integer | no | **[STARTER ONLY]** Extra pipeline minutes quota for this group |
+| Attribute | Type | Required | Description |
+| ------------------------------------ | ------- | -------- | ----------- |
+| `id` | integer | yes | The ID of the group. |
+| `name` | string | no | The name of the group. |
+| `path` | string | no | The path of the group. |
+| `description` | string | no | The description of the group. |
+| `membership_lock` | boolean | no | **(STARTER)** Prevent adding new members to project membership within this group. |
+| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
+| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
+| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
+| `request_access_enabled` | boolean | no | Allow users to request member access. |
+| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. |
+| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. |
+| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. |
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5?name=Experimental"
@@ -568,7 +573,7 @@ GET /groups?search=foobar
]
```
-## Sync group with LDAP **[CORE ONLY]**
+## Sync group with LDAP **(CORE ONLY)**
Syncs the group with its linked LDAP group. Only available to group owners and administrators.
@@ -584,7 +589,7 @@ Parameters:
Please consult the [Group Members](members.md) documentation.
-### Add LDAP group link **[CORE ONLY]**
+### Add LDAP group link **(CORE ONLY)**
Adds an LDAP group link.
@@ -599,7 +604,7 @@ Parameters:
- `group_access` (required) - Minimum access level for members of the LDAP group
- `provider` (required) - LDAP provider for the LDAP group
-### Delete LDAP group link **[CORE ONLY]**
+### Delete LDAP group link **(CORE ONLY)**
Deletes an LDAP group link.
diff --git a/doc/api/issue_links.md b/doc/api/issue_links.md
index 1c7db6a8e4c..280431fa87c 100644
--- a/doc/api/issue_links.md
+++ b/doc/api/issue_links.md
@@ -1,4 +1,4 @@
-# Issue links API **[STARTER]**
+# Issue links API **(STARTER)**
## List issue relations
@@ -67,7 +67,6 @@ POST /projects/:id/issues/:issue_iid/links
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
-
```json
{
"source_issue" : {
@@ -141,14 +140,12 @@ Deletes an issue link, thus removes the two-way relationship.
DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id
```
-
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `issue_link_id` | integer/string | yes | The ID of an issue relationship |
-
```json
{
"source_issue" : {
diff --git a/doc/api/issues.md b/doc/api/issues.md
index b29626525da..23126a05b66 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -43,12 +43,12 @@ GET /issues?confidential=true
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `author_username` | string | no | Return issues created by the given `username`. Simillar to `author_id` and mutually exclusive with `author_id`. |
+| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Simillar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
-| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
+| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search issues against their `title` and `description` |
@@ -57,7 +57,7 @@ GET /issues?confidential=true
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues
@@ -190,15 +190,15 @@ GET /groups/:id/issues?confidential=true
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:text_color`. Default is `false`. |
-| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
+| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `author_username` | string | no | Return issues created by the given `username`. Simillar to `author_id` and mutually exclusive with `author_id`. |
+| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Simillar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search group issues against their `title` and `description` |
@@ -206,7 +206,7 @@ GET /groups/:id/issues?confidential=true
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues
@@ -336,18 +336,18 @@ GET /projects/:id/issues?confidential=true
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
+| `iids[]` | integer array | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:text_color`. Default is `false`. |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `author_username` | string | no | Return issues created by the given `username`. Simillar to `author_id` and mutually exclusive with `author_id`. |
+| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. _([Introduced][ce-13004] in GitLab 9.5)_ |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Simillar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
-| `weight` **[STARTER]** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
+| `weight` **(STARTER)** | integer | no | Return issues with the specified `weight`. `None` returns issues with no weight assigned. `Any` returns issues with a weight assigned. |
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` |
@@ -355,7 +355,7 @@ GET /projects/:id/issues?confidential=true
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
@@ -596,14 +596,14 @@ POST /projects/:id/issues
| `title` | string | yes | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Set an issue to be confidential. Default is `false`. |
-| `assignee_ids` | Array[integer] | no | The ID of a user to assign issue |
+| `assignee_ids` | integer array | no | The ID of a user to assign issue |
| `milestone_id` | integer | no | The global ID of a milestone to assign issue |
| `labels` | string | no | Comma-separated label names for an issue |
| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project/group owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
| `merge_request_to_resolve_discussions_of` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values.|
| `discussion_to_resolve` | string | no | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. |
-| `weight` **[STARTER]** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. |
+| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues?title=Issues%20with%20auth&labels=bug
@@ -697,13 +697,13 @@ PUT /projects/:id/issues/:issue_iid
| `title` | string | no | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Updates an issue to be confidential |
-| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
+| `assignee_ids` | integer array | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
-| `weight` **[STARTER]** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0 |
+| `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0 |
| `discussion_locked` | boolean | no | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. |
```bash
diff --git a/doc/api/issues_statistics.md b/doc/api/issues_statistics.md
index 82bc9c142cc..d7edb296be2 100644
--- a/doc/api/issues_statistics.md
+++ b/doc/api/issues_statistics.md
@@ -34,16 +34,16 @@ GET /issues_statistics?confidential=true
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
-| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
+| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `search` | string | no | Search issues against their `title` and `description` |
| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description` |
| `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues_statistics
@@ -86,20 +86,20 @@ GET /groups/:id/issues_statistics?confidential=true
| ------------------- | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. |
-| `iids[]` | Array[integer] | no | Return only the issues having the given `iid` |
+| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `search` | string | no | Search group issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues_statistics
@@ -141,22 +141,21 @@ GET /projects/:id/issues_statistics?confidential=true
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `iids[]` | Array[integer] | no | Return only the milestone having the given `iid` |
+| `iids[]` | integer array | no | Return only the milestone having the given `iid` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | no | Return issues created by the given `username`. Similar to `author_id` and mutually exclusive with `author_id`. |
| `assignee_id` | integer | no | Return issues assigned to the given user `id`. Mutually exclusive with `assignee_username`. `None` returns unassigned issues. `Any` returns issues with an assignee. |
-| `assignee_username` | Array[String] | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
+| `assignee_username` | string array | no | Return issues assigned to the given `username`. Similar to `assignee_id` and mutually exclusive with `assignee_id`. In CE version `assignee_username` array should only contain a single value or an invalid param error will be returned otherwise. |
| `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. |
| `search` | string | no | Search project issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
-| `confidential ` | Boolean | no | Filter confidential or public issues. |
-
+| `confidential` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues_statistics
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 223bfed91a9..1add5f432ac 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -359,7 +359,7 @@ GET /projects/:id/jobs/:job_id/artifacts
|-------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `job_id` | integer | yes | ID of a job. |
-| `job_token` **[PREMIUM]** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
+| `job_token` **(PREMIUM)** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
Example request using the `PRIVATE-TOKEN` header:
@@ -368,7 +368,7 @@ curl --output artifacts.zip --header "PRIVATE-TOKEN: <your_access_token>" "https
```
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
-`.gitlab-ci.yml` **[PREMIUM]**, you can use either:
+`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
For example, the following job will download the artifacts of the job with ID
@@ -425,7 +425,7 @@ Parameters
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
| `job` | string | yes | The name of the job. |
-| `job_token` **[PREMIUM]** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
+| `job_token` **(PREMIUM)** | string | no | To be used with [triggers] for multi-project pipelines. It should be invoked only inside `.gitlab-ci.yml`. Its value is always `$CI_JOB_TOKEN`. |
Example request using the `PRIVATE-TOKEN` header:
@@ -434,7 +434,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
```
To use this in a [`script` definition](../ci/yaml/README.md#script) inside
-`.gitlab-ci.yml` **[PREMIUM]**, you can use either:
+`.gitlab-ci.yml` **(PREMIUM)**, you can use either:
- The `JOB-TOKEN` header with the GitLab-provided `CI_JOB_TOKEN` variable.
For example, the following job will download the artifacts of the `test` job
@@ -485,7 +485,7 @@ Parameters
| Attribute | Type | Required | Description |
|-----------------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `job_id ` | integer | yes | The unique job identifier. |
+| `job_id` | integer | yes | The unique job identifier. |
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
Example request:
@@ -782,7 +782,6 @@ DELETE /projects/:id/jobs/:job_id/artifacts
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `job_id` | integer | yes | ID of a job. |
-
Example request:
```sh
diff --git a/doc/api/license.md b/doc/api/license.md
index 2a8de64bdbf..12f1d03d576 100644
--- a/doc/api/license.md
+++ b/doc/api/license.md
@@ -1,4 +1,4 @@
-# License **[CORE ONLY]**
+# License **(CORE ONLY)**
In order to interact with license endpoints, you need to authenticate yourself
as an admin.
@@ -131,7 +131,6 @@ Returns:
- `201 Created` if the license is successfully added.
- `400 Bad Request` if the license couldn't be added, with an error message explaining the reason.
-
## Delete a license
```
diff --git a/doc/api/lint.md b/doc/api/lint.md
index 71c09d35b8c..b9b49f3df27 100644
--- a/doc/api/lint.md
+++ b/doc/api/lint.md
@@ -22,30 +22,30 @@ Example responses:
- Valid content:
- ```json
- {
- "status": "valid",
- "errors": []
- }
- ```
+ ```json
+ {
+ "status": "valid",
+ "errors": []
+ }
+ ```
- Invalid content:
- ```json
- {
- "status": "invalid",
- "errors": [
- "variables config should be a hash of key value pairs"
- ]
- }
- ```
+ ```json
+ {
+ "status": "invalid",
+ "errors": [
+ "variables config should be a hash of key value pairs"
+ ]
+ }
+ ```
- Without the content attribute:
- ```json
- {
- "error": "content is missing"
- }
- ```
+ ```json
+ {
+ "error": "content is missing"
+ }
+ ```
[ce-5953]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5953
diff --git a/doc/api/managed_licenses.md b/doc/api/managed_licenses.md
index 47b193111b6..1af7567626f 100644
--- a/doc/api/managed_licenses.md
+++ b/doc/api/managed_licenses.md
@@ -1,4 +1,4 @@
-# Managed Licenses API **[ULTIMATE]**
+# Managed Licenses API **(ULTIMATE)**
## List managed licenses
@@ -105,7 +105,7 @@ DELETE /projects/:id/managed_licenses/:managed_license_id
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/managed_licenses/4"
```
-When successful, it replies with an HTTP 204 response.
+When successful, it replies with an HTTP 204 response.
## Edit an existing managed license
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 49aaac06b46..c211916464a 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -1,4 +1,4 @@
-# Merge request approvals API **[STARTER]**
+# Merge request approvals API **(STARTER)**
Configuration for approvals on all Merge Requests (MR) in the project. Must be authenticated for all endpoints.
@@ -178,7 +178,6 @@ PUT /projects/:id/approvers
}
```
-
## Merge Request-level MR approvals
Configuration for approvals on a specific Merge Request. Must be authenticated for all endpoints.
@@ -250,7 +249,6 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
| `merge_request_iid` | integer | yes | The IID of MR |
| `approvals_required` | integer | yes | Approvals required before MR can be merged |
-
```json
{
"id": 5,
@@ -359,7 +357,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approve
| `id` | integer | yes | The ID of a project |
| `merge_request_iid` | integer | yes | The IID of MR |
| `sha` | string | no | The HEAD of the MR |
-| `approval_password` **[STARTER]** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/merge_request_approvals.md#require-authentication-when-approving-a-merge-request-starter) is enabled in the project settings. |
+| `approval_password` **(STARTER)** | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/merge_request_approvals.md#require-authentication-when-approving-a-merge-request-starter) is enabled in the project settings. |
The `sha` parameter works in the same way as
when [accepting a merge request](merge_requests.md#accept-mr): if it is passed, then it must
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 69db9f97a35..de87e4a0aee 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -44,7 +44,7 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
-| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
@@ -192,7 +192,7 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | yes | The ID of a project |
-| `iids[]` | Array[integer] | no | Return the request having the given `iid` |
+| `iids[]` | integer array | no | Return the request having the given `iid` |
| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
@@ -206,7 +206,7 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
@@ -358,7 +358,7 @@ Parameters:
| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. _([Introduced][ce-13060] in GitLab 9.5)_ |
-| `approver_ids` **[STARTER]** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `approver_ids` **(STARTER)** | Array[integer] | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
| `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. _([Introduced][ce-14016] in GitLab 10.0)_ |
| `source_branch` | string | no | Return merge requests with the given source branch |
| `target_branch` | string | no | Return merge requests with the given target branch |
@@ -831,12 +831,12 @@ POST /projects/:id/merge_requests
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `source_branch` | string | yes | The source branch |
| `target_branch` | string | yes | The target branch |
| `title` | string | yes | Title of MR |
| `assignee_id` | integer | no | Assignee user ID |
-| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
+| `assignee_ids` | integer array | no | The ID of the user(s) to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
| `description` | string | no | Description of MR |
| `target_project_id` | integer | no | The target project (numeric id) |
| `labels` | string | no | Labels for MR as a comma-separated list |
@@ -846,13 +846,13 @@ POST /projects/:id/merge_requests
| `allow_maintainer_to_push` | boolean | no | Deprecated, see allow_collaboration |
| `squash` | boolean | no | Squash commits into a single commit when merging |
-If `approvals_before_merge` **[STARTER]** is not provided, it inherits the value from the
+If `approvals_before_merge` **(STARTER)** is not provided, it inherits the value from the
target project. If it is provided, then the following conditions must hold in
order for it to take effect:
-1. The target project's `approvals_before_merge` must be greater than zero. (A
- value of zero disables approvals for that project.)
-2. The provided value of `approvals_before_merge` must be greater than the
+1. The target project's `approvals_before_merge` must be greater than zero. A
+ value of zero disables approvals for that project.
+1. The provided value of `approvals_before_merge` must be greater than the
target project's `approvals_before_merge`.
```json
@@ -987,7 +987,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| `target_branch` | string | no | The target branch |
| `title` | string | no | Title of MR |
| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
-| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
+| `assignee_ids` | integer array | no | The ID of the user(s) to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The global ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
| `labels` | string | no | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. |
| `description` | string | no | Description of MR |
@@ -1296,7 +1296,7 @@ the `approvals_before_merge` parameter:
## Merge to default merge ref path
-Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge`
+Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge`
ref, of the target project repository, if possible. This ref will have the state the target branch would have if
a regular merge action was taken.
@@ -1485,8 +1485,14 @@ PUT /projects/:id/merge_requests/:merge_request_iid/rebase
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/76/merge_requests/1/rebase
```
-This is an asynchronous request. The API will return an empty `202 Accepted`
-response if the request is enqueued successfully.
+This is an asynchronous request. The API will return a `202 Accepted` response
+if the request is enqueued successfully, with a response containing:
+
+```json
+{
+ "rebase_in_progress": true
+}
+```
You can poll the [Get single MR](#get-single-mr) endpoint with the
`include_rebase_in_progress` parameter to check the status of the
@@ -2255,6 +2261,6 @@ Example response:
[ce-15454]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15454
[ce-18935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18935
-## Approvals **[STARTER]**
+## Approvals **(STARTER)**
For approvals, please see [Merge Request Approvals](merge_request_approvals.md)
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index e745d0c2e6c..a6ded7d3bd2 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -16,13 +16,13 @@ GET /projects/:id/milestones?search=version
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `iids[]` | Array[integer] | optional | Return only the milestones having the given `iid` |
-| `state` | string | optional | Return only `active` or `closed` milestones |
-| `title` | string | optional | Return only the milestones having the given `title` |
-| `search` | string | optional | Return only milestones with a title or description matching the provided string |
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `iids[]` | integer array | optional | Return only the milestones having the given `iid` |
+| `state` | string | optional | Return only `active` or `closed` milestones |
+| `title` | string | optional | Return only the milestones having the given `title` |
+| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/milestones
@@ -148,7 +148,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a project milestone
-## Get all burndown chart events for a single milestone **[STARTER]**
+## Get all burndown chart events for a single milestone **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4737) in GitLab 12.1
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
index b354e2b9ab2..2b6eddf78a1 100644
--- a/doc/api/namespaces.md
+++ b/doc/api/namespaces.md
@@ -68,7 +68,7 @@ the `plan` parameter associated with a namespace:
]
```
-**Note**: Only group maintainers/owners are presented with `members_count_with_descendants`, as well as `plan` **[BRONZE ONLY]**.
+NOTE: **Note:** Only group maintainers/owners are presented with `members_count_with_descendants`, as well as `plan` **(BRONZE ONLY)**.
## Search for namespace
diff --git a/doc/api/notes.md b/doc/api/notes.md
index c09129c22d4..acbf0334563 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -5,7 +5,7 @@ Notes are comments on:
- Snippets
- Issues
- Merge requests
-- Epics **[ULTIMATE]**
+- Epics **(ULTIMATE)**
This includes system notes, which are notes about changes to the object (for example, when a milestone changes, there will be a corresponding system note). Label notes are not part of this API, but recorded as separate events in [resource label events](resource_label_events.md).
@@ -396,7 +396,7 @@ Parameters:
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/merge_requests/7/notes/1602
```
-## Epics **[ULTIMATE]**
+## Epics **(ULTIMATE)**
### List all epic notes
diff --git a/doc/api/notification_settings.md b/doc/api/notification_settings.md
index 0342622f384..c6667784617 100644
--- a/doc/api/notification_settings.md
+++ b/doc/api/notification_settings.md
@@ -1,8 +1,8 @@
# Notification settings API
->**Note:** This feature was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5632) in GitLab 8.12.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5632) in GitLab 8.12.
-**Valid notification levels**
+## Valid notification levels
The notification levels are defined in the `NotificationSetting.level` model enumeration. Currently, these levels are recognized:
@@ -31,8 +31,7 @@ If the `custom` level is used, specific email events can be controlled. Availabl
- `merge_merge_request`
- `failed_pipeline`
- `success_pipeline`
-- `new_epic` **[ULTIMATE]**
-
+- `new_epic` **(ULTIMATE)**
## Global notification settings
@@ -85,7 +84,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.
| `merge_merge_request` | boolean | no | Enable/disable this notification |
| `failed_pipeline` | boolean | no | Enable/disable this notification |
| `success_pipeline` | boolean | no | Enable/disable this notification |
-| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **[ULTIMATE]** |
+| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **(ULTIMATE)** |
Example response:
@@ -154,7 +153,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.
| `merge_merge_request` | boolean | no | Enable/disable this notification |
| `failed_pipeline` | boolean | no | Enable/disable this notification |
| `success_pipeline` | boolean | no | Enable/disable this notification |
-| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **[ULTIMATE]** |
+| `new_epic` | boolean | no | Enable/disable this notification ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6626) in 11.3) **(ULTIMATE)** |
Example responses:
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index dfe62554852..76e3a0fa1a4 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -192,7 +192,7 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token
```
-## Access GitLab API with `access token`
+## Access GitLab API with `access token`
The `access token` allows you to make requests to the API on behalf of a user.
You can pass the token either as GET parameter:
diff --git a/doc/api/packages.md b/doc/api/packages.md
index 618e5c3056a..ca90771b085 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -1,4 +1,4 @@
-# Packages API **[PREMIUM]**
+# Packages API **(PREMIUM)**
This is the API docs of [GitLab Packages](../administration/packages.md).
@@ -76,7 +76,7 @@ Example response:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9305) in GitLab 11.8.
-Get a list of package files of a single package.
+Get a list of package files of a single package.
```
GET /projects/:id/packages/:package_id/package_files
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 70fbe24099f..9678203eb40 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -1,6 +1,6 @@
# Pages domains API
-Endpoints for connecting custom domain(s) and TLS certificates in [GitLab Pages](https://about.gitlab.com/features/pages/).
+Endpoints for connecting custom domain(s) and TLS certificates in [GitLab Pages](https://about.gitlab.com/product/pages/).
The GitLab Pages feature must be enabled to use these endpoints. Find out more about [administering](../administration/pages/index.md) and [using](../user/project/pages/index.md) the feature.
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 470e55425f8..acf1ac90315 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -108,9 +108,9 @@ POST /projects/:id/pipeline_schedules
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `description` | string | yes | The description of pipeline schedule |
| `ref` | string | yes | The branch/tag name will be triggered |
-| `cron ` | string | yes | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
-| `cron_timezone ` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) (default: `'UTC'`) |
-| `active ` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: `true`) |
+| `cron` | string | yes | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
+| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) (default: `'UTC'`) |
+| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: `true`) |
```sh
curl --request POST --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form description="Build packages" --form ref="master" --form cron="0 1 * * 5" --form cron_timezone="UTC" --form active="true" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules"
@@ -153,9 +153,9 @@ PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
| `description` | string | no | The description of pipeline schedule |
| `ref` | string | no | The branch/tag name will be triggered |
-| `cron ` | string | no | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
-| `cron_timezone ` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) or `TZInfo::Timezone` (e.g. `America/Los_Angeles`) |
-| `active ` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially. |
+| `cron` | string | no | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
+| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) or `TZInfo::Timezone` (e.g. `America/Los_Angeles`) |
+| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially. |
```sh
curl --request PUT --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form cron="0 2 * * *" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13"
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 753faec3cc8..e36f74e7c77 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -132,11 +132,11 @@ Example of response
POST /projects/:id/pipeline
```
-| Attribute | Type | Required | Description |
-|------------|---------|----------|---------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `ref` | string | yes | Reference to commit |
-| `variables` | array | no | An array containing the variables available in the pipeline, matching the structure [{ 'key' => 'UPLOAD_TO_S3', 'variable_type' => 'file', 'value' => 'true' }] |
+| Attribute | Type | Required | Description |
+|-------------|---------|----------|---------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `ref` | string | yes | Reference to commit |
+| `variables` | array | no | An array containing the variables available in the pipeline, matching the structure `[{ 'key' => 'UPLOAD_TO_S3', 'variable_type' => 'file', 'value' => 'true' }]` |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/pipeline?ref=master"
diff --git a/doc/api/project_aliases.md b/doc/api/project_aliases.md
index 76343b4cd82..271632b61c3 100644
--- a/doc/api/project_aliases.md
+++ b/doc/api/project_aliases.md
@@ -1,4 +1,4 @@
-# Project Aliases API **[PREMIUM ONLY]**
+# Project Aliases API **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1.
diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md
index 3a7b3d8975e..1c382232837 100644
--- a/doc/api/project_badges.md
+++ b/doc/api/project_badges.md
@@ -91,7 +91,7 @@ POST /projects/:id/badges
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project ](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `link_url` | string | yes | URL of the badge link |
| `image_url` | string | yes | URL of the badge image |
diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md
index 327781f6c93..614ea41d572 100644
--- a/doc/api/project_clusters.md
+++ b/doc/api/project_clusters.md
@@ -167,7 +167,7 @@ Parameters:
| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate |
| `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project |
| `platform_kubernetes_attributes[authorization_type]` | String | no | The cluster authorization type: `rbac`, `abac` or `unknown_authorization`. Defaults to `rbac`. |
-| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **[PREMIUM]** |
+| `environment_scope` | String | no | The associated environment to the cluster. Defaults to `*` **(PREMIUM)** |
Example request:
@@ -257,7 +257,7 @@ Parameters:
| `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes |
| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate |
| `platform_kubernetes_attributes[namespace]` | String | no | The unique namespace related to the project |
-| `environment_scope` | String | no | The associated environment to the cluster **[PREMIUM]** |
+| `environment_scope` | String | no | The associated environment to the cluster **(PREMIUM)** |
NOTE: **Note:**
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index 66a749e4811..eab905bbc5f 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -74,7 +74,7 @@ POST /projects/:id/variables
| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
| `protected` | boolean | no | Whether the variable is protected |
| `masked` | boolean | no | Whether the variable is masked |
-| `environment_scope` | string | no | The `environment_scope` of the variable **[PREMIUM]** |
+| `environment_scope` | string | no | The `environment_scope` of the variable **(PREMIUM)** |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
@@ -108,7 +108,7 @@ PUT /projects/:id/variables/:key
| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
| `protected` | boolean | no | Whether the variable is protected |
| `masked` | boolean | no | Whether the variable is masked |
-| `environment_scope` | string | no | The `environment_scope` of the variable **[PREMIUM]** |
+| `environment_scope` | string | no | The `environment_scope` of the variable **(PREMIUM)** |
```
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/NEW_VARIABLE" --form "value=updated value"
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 2b2e40fb276..c1588f2292a 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -1,6 +1,6 @@
# Project snippets
-### Snippet visibility level
+## Snippet visibility level
Snippets in GitLab can be either private, internal or public.
You can set it with the `visibility` field in the snippet.
@@ -14,7 +14,7 @@ Constants for snippet visibility levels are:
| `public` | The snippet can be accessed without any authentication |
NOTE: **Note:**
-From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
+From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal`
visibility setting keep this setting. You can read more about the change in the
[relevant issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12388).
diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md
index 34d73abfcbf..2732fa47fa0 100644
--- a/doc/api/project_statistics.md
+++ b/doc/api/project_statistics.md
@@ -14,7 +14,7 @@ GET /projects/:id/statistics
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
Example response:
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
index 0a94a8d47ae..cc932e3ef58 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -16,7 +16,7 @@ Support will be added for [Issue and Merge Request templates](../user/project/de
in a future release.
Support for [Group-level file templates](../user/group/index.md#group-file-templates-premium)
-**[PREMIUM]** was [added](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987)
+**(PREMIUM)** was [added](https://gitlab.com/gitlab-org/gitlab-ee/issues/5987)
in GitLab 11.5
## Get all templates of a particular type
@@ -27,7 +27,7 @@ GET /projects/:id/templates/:type
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
Example response (licenses):
@@ -93,7 +93,7 @@ GET /projects/:id/templates/:type/:key
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `id` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `type` | string | yes| The type `(dockerfiles|gitignores|gitlab_ci_ymls|licenses)` of the template |
| `key` | string | yes | The key of the template, as obtained from the collection endpoint |
| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
diff --git a/doc/api/projects.md b/doc/api/projects.md
index c3e9fc69dd2..c199babc1be 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -55,8 +55,8 @@ GET /projects
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `with_programming_language` | string | no | Limit by projects which use the given programming language |
-| `wiki_checksum_failed` | boolean | no | **[PREMIUM]** Limit projects where the wiki checksum calculation has failed *([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2)* |
-| `repository_checksum_failed` | boolean | no | **[PREMIUM]** Limit projects where the repository checksum calculation has failed *([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2)* |
+| `wiki_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
+| `repository_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
When `simple=true` or the user is unauthenticated this returns something like:
@@ -741,12 +741,12 @@ POST /projects
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
-| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge requests by default |
-| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project |
-| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds |
+| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default |
+| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
+| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
| `initialize_with_readme` | boolean | no | `false` by default |
->**Note**: If your HTTP repository is not publicly accessible,
+NOTE: **Note:** If your HTTP repository is not publicly accessible,
add authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git`
where `password` is a public access key with the `api` scope enabled.
@@ -798,12 +798,12 @@ POST /projects/user/:user_id
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
-| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge requests by default |
-| `external_authorization_classification_label` | string | no | **[CORE ONLY]** The classification label for the project |
-| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project |
-| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds |
+| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge requests by default |
+| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
+| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
+| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
->**Note**: If your HTTP repository is not publicly accessible,
+NOTE: **Note:** If your HTTP repository is not publicly accessible,
add authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git`
where `password` is a public access key with the `api` scope enabled.
@@ -855,16 +855,16 @@ PUT /projects/:id
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for this project |
| `auto_devops_deploy_strategy` | string | no | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`) |
| `repository_storage` | string | no | Which storage shard the repository is on. Available only to admins |
-| `approvals_before_merge` | integer | no | **[STARTER]** How many approvers should approve merge request by default |
-| `external_authorization_classification_label` | string | no | **[CORE ONLY]** The classification label for the project |
-| `mirror` | boolean | no | **[STARTER]** Enables pull mirroring in a project |
-| `mirror_user_id` | integer | no | **[STARTER]** User responsible for all the activity surrounding a pull mirror event |
-| `mirror_trigger_builds` | boolean | no | **[STARTER]** Pull mirroring triggers builds |
-| `only_mirror_protected_branches` | boolean | no | **[STARTER]** Only mirror protected branches |
-| `mirror_overwrites_diverged_branches` | boolean | no | **[STARTER]** Pull mirror overwrites diverged branches |
-| `packages_enabled` | boolean | no | **[PREMIUM ONLY]** Enable or disable packages repository feature |
-
->**Note**: If your HTTP repository is not publicly accessible,
+| `approvals_before_merge` | integer | no | **(STARTER)** How many approvers should approve merge request by default |
+| `external_authorization_classification_label` | string | no | **(CORE ONLY)** The classification label for the project |
+| `mirror` | boolean | no | **(STARTER)** Enables pull mirroring in a project |
+| `mirror_user_id` | integer | no | **(STARTER)** User responsible for all the activity surrounding a pull mirror event |
+| `mirror_trigger_builds` | boolean | no | **(STARTER)** Pull mirroring triggers builds |
+| `only_mirror_protected_branches` | boolean | no | **(STARTER)** Only mirror protected branches |
+| `mirror_overwrites_diverged_branches` | boolean | no | **(STARTER)** Pull mirror overwrites diverged branches |
+| `packages_enabled` | boolean | no | **(PREMIUM ONLY)** Enable or disable packages repository feature |
+
+NOTE: **Note:** If your HTTP repository is not publicly accessible,
add authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git`
where `password` is a public access key with the `api` scope enabled.
@@ -1390,7 +1390,7 @@ Example response:
## Remove project
-Removes a project including all associated resources (issues, merge requests etc.)
+Removes a project including all associated resources (issues, merge requests etc).
```
DELETE /projects/:id
@@ -1648,7 +1648,7 @@ POST /projects/:id/housekeeping
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
-## Push Rules **[STARTER]**
+## Push Rules **(STARTER)**
### Get project push rules
@@ -1667,6 +1667,7 @@ GET /projects/:id/push_rule
"id": 1,
"project_id": 3,
"commit_message_regex": "Fixes \d+\..*",
+ "commit_message_negative_regex": "ssh\:\/\/",
"branch_name_regex": "",
"deny_delete_tag": false,
"created_at": "2012-10-12T17:04:47Z",
@@ -1679,10 +1680,17 @@ GET /projects/:id/push_rule
}
```
-The following attributes are restricted to certain plans, and will not appear if
-you do not have access to those features:
+Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will also see
+the `commit_committer_check` parameter:
-* `commit_committer_check` only available on **[PREMIUM]**
+```json
+{
+ "id": 1,
+ "project_id": 3,
+ "commit_committer_check": false
+ ...
+}
+```
### Add project push rule
@@ -1692,18 +1700,19 @@ Adds a push rule to a specified project.
POST /projects/:id/push_rule
```
-| Attribute | Type | Required | Description |
-| -------------------------------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
-| `deny_delete_tag` **[STARTER]** | boolean | no | Deny deleting a tag |
-| `member_check` **[STARTER]** | boolean | no | Restrict commits by author (email) to existing GitLab users |
-| `prevent_secrets` **[STARTER]** | boolean | no | GitLab will reject any files that are likely to contain secrets |
-| `commit_message_regex` **[STARTER]** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` |
-| `branch_name_regex` **[STARTER]** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` |
-| `author_email_regex` **[STARTER]** | string | no | All commit author emails must match this, e.g. `@my-company.com$` |
-| `file_name_regex` **[STARTER]** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
-| `max_file_size` **[STARTER]** | integer | no | Maximum file size (MB) |
-| `commit_committer_check` **[PREMIUM]** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
+| Attribute | Type | Required | Description |
+| --------------------------------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
+| `member_check` **(STARTER)** | boolean | no | Restrict commits by author (email) to existing GitLab users |
+| `prevent_secrets` **(STARTER)** | boolean | no | GitLab will reject any files that are likely to contain secrets |
+| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` |
+| `commit_message_negative_regex` **(STARTER)** | string | no | No commit message is allowed to match this, e.g. `ssh\:\/\/` |
+| `branch_name_regex` **(STARTER)** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` |
+| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match this, e.g. `@my-company.com$` |
+| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
+| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) |
+| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
### Edit project push rule
@@ -1713,18 +1722,19 @@ Edits a push rule for a specified project.
PUT /projects/:id/push_rule
```
-| Attribute | Type | Required | Description |
-| -------------------------------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
-| `deny_delete_tag` **[STARTER]** | boolean | no | Deny deleting a tag |
-| `member_check` **[STARTER]** | boolean | no | Restrict commits by author (email) to existing GitLab users |
-| `prevent_secrets` **[STARTER]** | boolean | no | GitLab will reject any files that are likely to contain secrets |
-| `commit_message_regex` **[STARTER]** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` |
-| `branch_name_regex` **[STARTER]** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` |
-| `author_email_regex` **[STARTER]** | string | no | All commit author emails must match this, e.g. `@my-company.com$` |
-| `file_name_regex` **[STARTER]** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
-| `max_file_size` **[STARTER]** | integer | no | Maximum file size (MB) |
-| `commit_committer_check` **[PREMIUM]** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
+| Attribute | Type | Required | Description |
+| --------------------------------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
+| `member_check` **(STARTER)** | boolean | no | Restrict commits by author (email) to existing GitLab users |
+| `prevent_secrets` **(STARTER)** | boolean | no | GitLab will reject any files that are likely to contain secrets |
+| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match this, e.g. `Fixed \d+\..*` |
+| `commit_message_negative_regex` **(STARTER)** | string | no | No commit message is allowed to match this, e.g. `ssh\:\/\/` |
+| `branch_name_regex` **(STARTER)** | string | no | All branch names must match this, e.g. `(feature|hotfix)\/*` |
+| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match this, e.g. `@my-company.com$` |
+| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
+| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) |
+| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
### Delete project push rule
@@ -1765,7 +1775,7 @@ Read more in the [Project import/export](project_import_export.md) documentation
Read more in the [Project members](members.md) documentation.
-## Start the pull mirroring process for a Project **[STARTER]**
+## Start the pull mirroring process for a Project **(STARTER)**
> Introduced in [GitLab Starter](https://about.gitlab.com/pricing) 10.3.
diff --git a/doc/api/protected_branches.md b/doc/api/protected_branches.md
index 6e41584afef..9309306ba05 100644
--- a/doc/api/protected_branches.md
+++ b/doc/api/protected_branches.md
@@ -166,9 +166,9 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitla
| `push_access_level` | string | no | Access levels allowed to push (defaults: `40`, maintainer access level) |
| `merge_access_level` | string | no | Access levels allowed to merge (defaults: `40`, maintainer access level) |
| `unprotect_access_level` | string | no | Access levels allowed to unprotect (defaults: `40`, maintainer access level) |
-| `allowed_to_push` | array | no | **[STARTER]** Array of access levels allowed to push, with each described by a hash |
-| `allowed_to_merge` | array | no | **[STARTER]** Array of access levels allowed to merge, with each described by a hash |
-| `allowed_to_unprotect` | array | no | **[STARTER]**Array of access levels allowed to unprotect, with each described by a hash |
+| `allowed_to_push` | array | no | **(STARTER)** Array of access levels allowed to push, with each described by a hash |
+| `allowed_to_merge` | array | no | **(STARTER)** Array of access levels allowed to merge, with each described by a hash |
+| `allowed_to_unprotect` | array | no | **(STARTER)**Array of access levels allowed to unprotect, with each described by a hash |
Example response:
@@ -229,7 +229,7 @@ Example response:
}
```
-### Example with user / group level access **[STARTER]**
+### Example with user / group level access **(STARTER)**
Elements in the `allowed_to_push` / `allowed_to_merge` / `allowed_to_unprotect` array should take the
form `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}`. Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md). These access levels allow [more granular control over protected branch access](../user/project/protected_branches.md#restricting-push-and-merge-access-to-certain-users-starter) and were [added to the API in ][ee-3516] in GitLab 10.3 EE.
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 681dc72c934..ffae5c17310 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -121,9 +121,9 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (optional) - The commit SHA to download. A tag, branch reference, or SHA can be used. This defaults to the tip of the default branch if not specified. For example:
- ```sh
- curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.com/api/v4/projects/<project_id>/repository/archive?sha=<commit_sha>
- ```
+```sh
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.com/api/v4/projects/<project_id>/repository/archive?sha=<commit_sha>
+```
## Compare branches, tags or commits
diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md
index f0a7ac4e41d..7ad4d78014c 100644
--- a/doc/api/resource_label_events.md
+++ b/doc/api/resource_label_events.md
@@ -88,7 +88,7 @@ Parameters:
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/5/issues/11/resource_label_events/1
```
-## Epics **[ULTIMATE]**
+## Epics **(ULTIMATE)**
### List group epic label events
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 2d91428d1c1..e6962d17a98 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -20,10 +20,10 @@ Here's an example of how the two tokens are used in Runner registration:
1. You use that authentication token and add it to the
[Runner's configuration file](https://docs.gitlab.com/runner/commands/#configuration-file):
- ```toml
- [[runners]]
- token = "<authentication_token>"
- ```
+ ```toml
+ [[runners]]
+ token = "<authentication_token>"
+ ```
GitLab and Runner are then connected.
@@ -44,7 +44,7 @@ GET /runners?tag_list=tag1,tag2
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
-| `tag_list` | Array[String] | no | List of of the runner's tags |
+| `tag_list` | string array | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
@@ -95,7 +95,7 @@ GET /runners/all?tag_list=tag1,tag2
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
-| `tag_list` | Array[String] | no | List of of the runner's tags |
+| `tag_list` | string array | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/all"
@@ -214,10 +214,10 @@ PUT /runners/:id
| `description` | string | no | The description of a runner |
| `active` | boolean | no | The state of a runner; can be set to `true` or `false` |
| `tag_list` | array | no | The list of tags for a runner; put array of tags, that should be finally assigned to a runner |
-| `run_untagged` | boolean | no | Flag indicating the runner can execute untagged jobs |
-| `locked` | boolean | no | Flag indicating the runner is locked |
-| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
+| `run_untagged`| boolean | no | Flag indicating the runner can execute untagged jobs |
+| `locked` | boolean | no | Flag indicating the runner is locked |
+| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
+| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
```
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
@@ -291,6 +291,8 @@ GET /runners/:id/jobs
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a runner |
| `status` | string | no | Status of the job; one of: `running`, `success`, `failed`, `canceled` |
+| `order_by`| string | no | Order jobs by `id`. |
+| `sort` | string | no | Sort jobs in `asc` or `desc` order (default: `desc`) |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/1/jobs?status=running"
@@ -385,7 +387,7 @@ GET /projects/:id/runners?tag_list=tag1,tag2
| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
-| `tag_list` | Array[String] | no | List of of the runner's tags |
+| `tag_list` | string array | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/runners"
@@ -477,17 +479,17 @@ Register a new Runner for the instance.
POST /runners
```
-| Attribute | Type | Required | Description |
-|-------------|---------|----------|---------------------|
-| `token` | string | yes | [Registration token](#registration-and-authentication-tokens). |
-| `description`| string | no | Runner's description|
-| `info` | hash | no | Runner's metadata |
-| `active` | boolean| no | Whether the Runner is active |
-| `locked` | boolean| no | Whether the Runner should be locked for current project |
-| `run_untagged` | boolean | no | Whether the Runner should handle untagged jobs |
-| `tag_list` | Array[String] | no | List of Runner's tags |
-| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
-| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
+| Attribute | Type | Required | Description |
+|--------------|---------|----------|---------------------|
+| `token` | string | yes | [Registration token](#registration-and-authentication-tokens). |
+| `description`| string | no | Runner's description|
+| `info` | hash | no | Runner's metadata |
+| `active` | boolean | no | Whether the Runner is active |
+| `locked` | boolean | no | Whether the Runner should be locked for current project |
+| `run_untagged` | boolean | no | Whether the Runner should handle untagged jobs |
+| `tag_list` | string array | no | List of Runner's tags |
+| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
+| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
```
curl --request POST "https://gitlab.example.com/api/v4/runners" --form "token=<registration_token>" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
diff --git a/doc/api/scim.md b/doc/api/scim.md
index 3870ea788e7..ece7f56e394 100644
--- a/doc/api/scim.md
+++ b/doc/api/scim.md
@@ -1,4 +1,4 @@
-# SCIM API **[SILVER ONLY]**
+# SCIM API **(SILVER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9388) in [GitLab Silver](https://about.gitlab.com/pricing/) 11.10.
@@ -6,7 +6,7 @@ The SCIM API implements the [the RFC7644 protocol](https://tools.ietf.org/html/r
NOTE: **Note:**
[Group SSO](../user/group/saml_sso/index.md) and the feature
-flag `:group_scim` must be enabled for the group. For more information, see [SCIM setup documentation](../user/group/saml_sso/scim_setup.md#requirements).
+flag `:group_scim` must be enabled for the group. For more information, see [SCIM setup documentation](../user/group/saml_sso/scim_setup.md#requirements).
## Get a list of SAML users
diff --git a/doc/api/search.md b/doc/api/search.md
index abb77ae05dc..60acf600ac7 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -19,7 +19,7 @@ GET /search
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs, users.
-If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **[STARTER]**
+If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **(STARTER)**
The response depends on the requested scope.
@@ -283,7 +283,7 @@ Example response:
]
```
-### Scope: wiki_blobs **[STARTER]**
+### Scope: wiki_blobs **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
@@ -308,7 +308,7 @@ Example response:
]
```
-### Scope: commits **[STARTER]**
+### Scope: commits **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
@@ -341,7 +341,7 @@ Example response:
]
```
-### Scope: blobs **[STARTER]**
+### Scope: blobs **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
@@ -415,7 +415,7 @@ GET /groups/:id/search
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, users.
-If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **[STARTER]**
+If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). **(STARTER)**
The response depends on the requested scope.
@@ -617,7 +617,7 @@ Example response:
]
```
-### Scope: wiki_blobs **[STARTER]**
+### Scope: wiki_blobs **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
@@ -642,7 +642,7 @@ Example response:
]
```
-### Scope: commits **[STARTER]**
+### Scope: commits **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
@@ -675,7 +675,7 @@ Example response:
]
```
-### Scope: blobs **[STARTER]**
+### Scope: blobs **(STARTER)**
This scope is available only if [Elasticsearch](../integration/elasticsearch.md) is enabled.
diff --git a/doc/api/services.md b/doc/api/services.md
index c811d0e84ca..df15e6892b0 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -583,7 +583,7 @@ Parameters:
| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
-| `jira_issue_transition_id` | string | no | The ID of a transition that moves issues to a closed state. You can find this number under the Jira workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`. |
+| `jira_issue_transition_id` | string | no | The ID of a transition that moves issues to a closed state. You can find this number under the Jira workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column. By default, this ID is set to `2`. |
| `commit_events` | boolean | false | Enable notifications for commit events |
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
@@ -1146,7 +1146,7 @@ Get JetBrains TeamCity CI service settings for a project.
GET /projects/:id/services/teamcity
```
-## Jenkins CI **[STARTER]**
+## Jenkins CI **(STARTER)**
A continuous integration and build server
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 876a5a75590..ff48cac1f47 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -68,11 +68,16 @@ Example response:
```
Users on GitLab [Premium or Ultimate](https://about.gitlab.com/pricing/) may also see
-the `file_template_project_id` or the `geo_node_allowed_ips` parameters: **[PREMIUM ONLY]**
+the `file_template_project_id` or the `geo_node_allowed_ips` parameters:
```json
+{
+ "id" : 1,
+ "signup_enabled" : true,
"file_template_project_id": 1,
"geo_node_allowed_ips": "0.0.0.0/0, ::/0"
+ ...
+}
```
## Change application settings
@@ -145,7 +150,7 @@ these parameters:
- `file_template_project_id`
- `geo_node_allowed_ips`
-Example responses: **[PREMIUM ONLY]**
+Example responses: **(PREMIUM ONLY)**
```json
"external_authorization_service_enabled": true,
@@ -169,12 +174,12 @@ are listed in the descriptions of the relevant settings.
| `after_sign_up_text` | string | no | Text shown to the user after signing up |
| `akismet_api_key` | string | required by: `akismet_enabled` | API key for akismet spam protection. |
| `akismet_enabled` | boolean | no | (**If enabled, requires:** `akismet_api_key`) Enable or disable akismet spam protection. |
-| `allow_group_owners_to_manage_ldap` | boolean | no | **[Premium]** Set to `true` to allow group owners to manage LDAP |
+| `allow_group_owners_to_manage_ldap` | boolean | no | **(PREMIUM)** Set to `true` to allow group owners to manage LDAP |
| `allow_local_requests_from_hooks_and_services` | boolean | no | Allow requests to the local network from hooks and services. |
| `authorized_keys_enabled` | boolean | no | By default, we write to the `authorized_keys` file to support Git over SSH without additional configuration. GitLab can be optimized to authenticate SSH keys via the database file. Only disable this if you have configured your OpenSSH server to use the AuthorizedKeysCommand. |
| `auto_devops_domain` | string | no | Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages. |
| `auto_devops_enabled` | boolean | no | Enable Auto DevOps for projects by default. It will automatically build, test, and deploy applications based on a predefined CI/CD configuration. |
-| `check_namespace_plan` | boolean | no | **[Premium]** Enabling this will make only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. |
+| `check_namespace_plan` | boolean | no | **(PREMIUM)** Enabling this will make only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. |
| `clientside_sentry_dsn` | string | required by: `clientside_sentry_enabled` | Clientside Sentry Data Source Name. |
| `clientside_sentry_enabled` | boolean | no | (**If enabled, requires:** `clientside_sentry_dsn`) Enable Sentry error reporting for the client side. |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
@@ -191,31 +196,31 @@ are listed in the descriptions of the relevant settings.
| `dsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded DSA key. Default is `0` (no restriction). `-1` disables DSA keys. |
| `ecdsa_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ECDSA key. Default is `0` (no restriction). `-1` disables ECDSA keys. |
| `ed25519_key_restriction` | integer | no | The minimum allowed curve size (in bits) of an uploaded ED25519 key. Default is `0` (no restriction). `-1` disables ED25519 keys. |
-| `elasticsearch_aws` | boolean | no | **[Premium]** Enable the use of AWS hosted Elasticsearch |
-| `elasticsearch_aws_access_key` | string | no | **[Premium]** AWS IAM access key |
-| `elasticsearch_aws_region` | string | no | **[Premium]** The AWS region the elasticsearch domain is configured |
-| `elasticsearch_aws_secret_access_key` | string | no | **[Premium]** AWS IAM secret access key |
-| `elasticsearch_experimental_indexer` | boolean | no | **[Premium]** Use the experimental elasticsearch indexer. More info: <https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer> |
-| `elasticsearch_indexing` | boolean | no | **[Premium]** Enable Elasticsearch indexing |
-| `elasticsearch_search` | boolean | no | **[Premium]** Enable Elasticsearch search |
-| `elasticsearch_url` | string | no | **[Premium]** The url to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (e.g., `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). |
-| `elasticsearch_limit_indexing` | boolean | no | **[Premium]** Limit Elasticsearch to index certain namespaces and projects |
-| `elasticsearch_project_ids` | array of integers | no | **[Premium]** The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
-| `elasticsearch_namespace_ids` | array of integers | no | **[Premium]** The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
-| `email_additional_text` | string | no | **[Premium]** Additional text added to the bottom of every email for legal/auditing/compliance reasons |
+| `elasticsearch_aws` | boolean | no | **(PREMIUM)** Enable the use of AWS hosted Elasticsearch |
+| `elasticsearch_aws_access_key` | string | no | **(PREMIUM)** AWS IAM access key |
+| `elasticsearch_aws_region` | string | no | **(PREMIUM)** The AWS region the elasticsearch domain is configured |
+| `elasticsearch_aws_secret_access_key` | string | no | **(PREMIUM)** AWS IAM secret access key |
+| `elasticsearch_experimental_indexer` | boolean | no | **(PREMIUM)** Use the experimental elasticsearch indexer. More info: <https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer> |
+| `elasticsearch_indexing` | boolean | no | **(PREMIUM)** Enable Elasticsearch indexing |
+| `elasticsearch_search` | boolean | no | **(PREMIUM)** Enable Elasticsearch search |
+| `elasticsearch_url` | string | no | **(PREMIUM)** The url to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (e.g., `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). |
+| `elasticsearch_limit_indexing` | boolean | no | **(PREMIUM)** Limit Elasticsearch to index certain namespaces and projects |
+| `elasticsearch_project_ids` | array of integers | no | **(PREMIUM)** The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
+| `elasticsearch_namespace_ids` | array of integers | no | **(PREMIUM)** The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
+| `email_additional_text` | string | no | **(PREMIUM)** Additional text added to the bottom of every email for legal/auditing/compliance reasons |
| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `enforce_terms` | boolean | no | (**If enabled, requires:** `terms`) Enforce application ToS to all users. |
-| `external_auth_client_cert` | string | no | **[Premium]** (**If enabled, requires:** `external_auth_client_key`) The certificate to use to authenticate with the external authorization service |
-| `external_auth_client_key` | string | required by: `external_auth_client_cert` | **[Premium]** Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored |
-| `external_auth_client_key_pass` | string | no | **[Premium]** Passphrase to use for the private key when authenticating with the external service this is encrypted when stored |
-| `external_authorization_service_enabled` | boolean | no | **[Premium]** (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects |
-| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | **[Premium]** The default classification label to use when requesting authorization and no classification label has been specified on the project |
-| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **[Premium]** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) |
-| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **[Premium]** URL to which authorization requests will be directed |
-| `file_template_project_id` | integer | no | **[Premium]** The ID of a project to load custom file templates from |
+| `external_auth_client_cert` | string | no | **(PREMIUM)** (**If enabled, requires:** `external_auth_client_key`) The certificate to use to authenticate with the external authorization service |
+| `external_auth_client_key` | string | required by: `external_auth_client_cert` | **(PREMIUM)** Private key for the certificate when authentication is required for the external authorization service, this is encrypted when stored |
+| `external_auth_client_key_pass` | string | no | **(PREMIUM)** Passphrase to use for the private key when authenticating with the external service this is encrypted when stored |
+| `external_authorization_service_enabled` | boolean | no | **(PREMIUM)** (**If enabled, requires:** `external_authorization_service_default_label`, `external_authorization_service_timeout` and `external_authorization_service_url` ) Enable using an external authorization service for accessing projects |
+| `external_authorization_service_default_label` | string | required by: `external_authorization_service_enabled` | **(PREMIUM)** The default classification label to use when requesting authorization and no classification label has been specified on the project |
+| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **(PREMIUM)** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) |
+| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **(PREMIUM)** URL to which authorization requests will be directed |
+| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from |
| `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. |
-| `geo_status_timeout` | integer | no | **[Premium]** The amount of seconds after which a request to get a secondary node status will time out. |
+| `geo_status_timeout` | integer | no | **(PREMIUM)** The amount of seconds after which a request to get a secondary node status will time out. |
| `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. |
| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
| `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. |
@@ -224,7 +229,7 @@ are listed in the descriptions of the relevant settings.
| `help_page_hide_commercial_content` | boolean | no | Hide marketing-related entries from help. |
| `help_page_support_url` | string | no | Alternate support URL for help page. |
| `help_page_text` | string | no | Custom text displayed on the help page. |
-| `help_text` | string | no | **[Premium]** GitLab server administrator information |
+| `help_text` | string | no | **(PREMIUM)** GitLab server administrator information |
| `hide_third_party_offers` | boolean | no | Do not display offers from third parties within GitLab. |
| `home_page_url` | string | no | Redirect to this URL when not logged in. |
| `housekeeping_bitmaps_enabled` | boolean | required by: `housekeeping_enabled` | Enable Git pack file bitmap creation. |
@@ -247,9 +252,9 @@ are listed in the descriptions of the relevant settings.
| `metrics_sample_interval` | integer | required by: `metrics_enabled` | The sampling interval in seconds. |
| `metrics_timeout` | integer | required by: `metrics_enabled` | The amount of seconds after which InfluxDB will time out. |
| `mirror_available` | boolean | no | Allow mirrors to be set up for projects. If disabled, only admins will be able to set up mirrors in projects. |
-| `mirror_capacity_threshold` | integer | no | **[Premium]** Minimum capacity to be available before scheduling more mirrors preemptively |
-| `mirror_max_capacity` | integer | no | **[Premium]** Maximum number of mirrors that can be synchronizing at the same time. |
-| `mirror_max_delay` | integer | no | **[Premium]** Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. |
+| `mirror_capacity_threshold` | integer | no | **(PREMIUM)** Minimum capacity to be available before scheduling more mirrors preemptively |
+| `mirror_max_capacity` | integer | no | **(PREMIUM)** Maximum number of mirrors that can be synchronizing at the same time. |
+| `mirror_max_delay` | integer | no | **(PREMIUM)** Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. |
| `pages_domain_verification_enabled` | boolean | no | Require users to prove ownership of custom domains. Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled. |
| `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. |
| `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. |
@@ -261,12 +266,12 @@ are listed in the descriptions of the relevant settings.
| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to `0` to disable polling. |
| `project_export_enabled` | boolean | no | Enable project export. |
| `prometheus_metrics_enabled` | boolean | no | Enable prometheus metrics. |
-| `pseudonymizer_enabled` | boolean | no | **[Premium]** When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory.
+| `pseudonymizer_enabled` | boolean | no | **(PREMIUM)** When enabled, GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory.
| `recaptcha_enabled` | boolean | no | (**If enabled, requires:** `recaptcha_private_key` and `recaptcha_site_key`) Enable recaptcha. |
| `recaptcha_private_key` | string | required by: `recaptcha_enabled` | Private key for recaptcha. |
| `recaptcha_site_key` | string | required by: `recaptcha_enabled` | Site key for recaptcha. |
| `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
-| `repository_size_limit` | integer | no | **[Premium]** Size limit per repository (MB) |
+| `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) |
| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
@@ -274,15 +279,15 @@ are listed in the descriptions of the relevant settings.
| `send_user_confirmation_email` | boolean | no | Send confirmation email on sign-up. |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
| `shared_runners_enabled` | boolean | no | (**If enabled, requires:** `shared_runners_text` and `shared_runners_minutes`) Enable shared runners for new projects. |
-| `shared_runners_minutes` | integer | required by: `shared_runners_enabled` | **[Premium]** Set the maximum number of pipeline minutes that a group can use on shared Runners per month. |
+| `shared_runners_minutes` | integer | required by: `shared_runners_enabled` | **(PREMIUM)** Set the maximum number of pipeline minutes that a group can use on shared Runners per month. |
| `shared_runners_text` | string | required by: `shared_runners_enabled` | Shared runners text. |
| `sign_in_text` | string | no | Text on the login page. |
| `signin_enabled` | string | no | (Deprecated: Use `password_authentication_enabled_for_web` instead) Flag indicating if password authentication is enabled for the web interface. |
| `signup_enabled` | boolean | no | Enable registration. Default is `true`. |
-| `slack_app_enabled` | boolean | no | **[Premium]** (**If enabled, requires:** `slack_app_id`, `slack_app_secret` and `slack_app_secret`) Enable Slack app. |
-| `slack_app_id` | string | required by: `slack_app_enabled` | **[Premium]** The app id of the Slack-app. |
-| `slack_app_secret` | string | required by: `slack_app_enabled` | **[Premium]** The app secret of the Slack-app. |
-| `slack_app_verification_token` | string | required by: `slack_app_enabled` | **[Premium]** The verification token of the Slack-app. |
+| `slack_app_enabled` | boolean | no | **(PREMIUM)** (**If enabled, requires:** `slack_app_id`, `slack_app_secret` and `slack_app_secret`) Enable Slack app. |
+| `slack_app_id` | string | required by: `slack_app_enabled` | **(PREMIUM)** The app id of the Slack-app. |
+| `slack_app_secret` | string | required by: `slack_app_enabled` | **(PREMIUM)** The app secret of the Slack-app. |
+| `slack_app_verification_token` | string | required by: `slack_app_enabled` | **(PREMIUM)** The verification token of the Slack-app. |
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
| `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. |
| `throttle_authenticated_api_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_api_period_in_seconds` and `throttle_authenticated_api_requests_per_period`) Enable authenticated API request rate limit. Helps reduce request volume (e.g. from crawlers or abusive bots). |
@@ -305,4 +310,4 @@ are listed in the descriptions of the relevant settings.
| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. |
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `local_markdown_version` | integer | no | Increase this value when any cached markdown should be invalidated. |
-| `geo_node_allowed_ips` | string | yes | **[Premium]** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. |
+| `geo_node_allowed_ips` | string | yes | **(PREMIUM)** Comma-separated list of IPs and CIDRs of allowed secondary nodes. For example, `1.1.1.1, 2.2.2.0/24`. |
diff --git a/doc/api/users.md b/doc/api/users.md
index 5615dcdd307..213d1865aca 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -70,11 +70,11 @@ Username search is case insensitive.
GET /users
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `order_by` | string | no | Return users ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
-| `sort` | string | no | Return users sorted in `asc` or `desc` order. Default is `desc` |
-| `two_factor` | string | no | Filter users by Two-factor authentication. Filter values are `enabled` or `disabled`. By default it returns all users |
+| Attribute | Type | Required | Description |
+| ------------ | ------ | -------- | ----------- |
+| `order_by` | string | no | Return users ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
+| `sort` | string | no | Return users sorted in `asc` or `desc` order. Default is `desc` |
+| `two_factor` | string | no | Filter users by Two-factor authentication. Filter values are `enabled` or `disabled`. By default it returns all users |
```json
[
@@ -147,6 +147,25 @@ GET /users
]
```
+Users on GitLab [Silver or higher](https://about.gitlab.com/pricing/) will also see
+the `group_saml` provider option:
+
+```json
+[
+ {
+ "id": 1,
+ ...
+ "identities": [
+ {"provider": "github", "extern_uid": "2435223452345"},
+ {"provider": "bitbucket", "extern_uid": "john.smith"},
+ {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"},
+ {"provider": "group_saml", "extern_uid": "123789", "saml_provider_id": 10}
+ ],
+ ...
+ }
+]
+```
+
You can lookup users by external UID and provider:
```
@@ -260,14 +279,25 @@ Example Responses:
"can_create_project": true,
"two_factor_enabled": true,
"external": false,
- "private_profile": false,
- "shared_runners_minutes_limit": 133
- "extra_shared_runners_minutes_limit": 133
+ "private_profile": false
}
```
Users on GitLab [Starter, Bronze, or higher](https://about.gitlab.com/pricing/) will also see
-the `shared_runners_minutes_limit` and `extra_shared_runners_minutes_limit` parameters: **[STARTER]**
+the `shared_runners_minutes_limit` and `extra_shared_runners_minutes_limit` parameters.
+
+```json
+{
+ "id": 1,
+ "username": "john_smith",
+ "shared_runners_minutes_limit": 133,
+ "extra_shared_runners_minutes_limit": 133
+ ...
+}
+```
+
+Users on GitLab.com [Silver, or higher](https://about.gitlab.com/pricing/) will also
+see the `group_saml` option:
```json
{
@@ -275,6 +305,12 @@ the `shared_runners_minutes_limit` and `extra_shared_runners_minutes_limit` para
"username": "john_smith",
"shared_runners_minutes_limit": 133,
"extra_shared_runners_minutes_limit": 133
+ "identities": [
+ {"provider": "github", "extern_uid": "2435223452345"},
+ {"provider": "bitbucket", "extern_uid": "john.smith"},
+ {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"},
+ {"provider": "group_saml", "extern_uid": "123789", "saml_provider_id": 10}
+ ],
...
}
```
@@ -324,8 +360,8 @@ Parameters:
- `external` (optional) - Flags the user as external - true or false(default)
- `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false
-- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **[STARTER]**
-- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **[STARTER]**
+- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)**
+- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
## User modification
@@ -361,8 +397,8 @@ Parameters:
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user
- `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false
-- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **[STARTER]**
-- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **[STARTER]**
+- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)**
+- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
On password update, user will be forced to change it upon next login.
Note, at the moment this method does only return a `404` error,
@@ -385,9 +421,7 @@ Parameters:
[moved to the ghost user](../user/profile/account/delete_account.md#associated-records)
will be deleted instead, as well as groups owned solely by this user.
-## User
-
-### For normal users
+## List current user (for normal users)
Gets currently authenticated user.
@@ -433,7 +467,7 @@ GET /user
}
```
-### For admins
+## List current user (for admins)
Parameters:
@@ -512,9 +546,9 @@ Get the status of a user.
GET /users/:id_or_username/status
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id_or_username` | string | yes | The id or username of the user to get a status of |
+| Attribute | Type | Required | Description |
+| ---------------- | ------ | -------- | ----------- |
+| `id_or_username` | string | yes | The id or username of the user to get a status of |
```bash
curl "https://gitlab.example.com/users/janedoe/status"
@@ -538,8 +572,8 @@ Set the status of the current user.
PUT /user/status
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | ----------- |
| `emoji` | string | no | The name of the emoji to use as status, if omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index][gemojione-index]. |
| `message` | string | no | The message to set as a status. It can also contain emoji codes. |
@@ -559,9 +593,33 @@ Example responses
}
```
+## User counts
+
+Get the counts (same as in top right menu) of the currently signed in user.
+
+| Attribute | Type | Description |
+| --------- | ---- | ----------- |
+| `merge_requests` | number | Merge requests that are active and assigned to current user. |
+
+```
+GET /user_counts
+```
+
+```bash
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/user_counts"
+```
+
+Example response:
+
+```json
+{
+ "merge_requests": 4
+}
+```
+
## List user projects
-Please refer to the [List of user projects ](projects.md#list-user-projects).
+Please refer to the [List of user projects](projects.md#list-user-projects).
## List SSH keys
@@ -737,9 +795,9 @@ GET /user/gpg_keys/:key_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `key_id` | integer | yes | The ID of the GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `key_id` | integer | yes | The ID of the GPG key |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/user/gpg_keys/1
@@ -765,9 +823,9 @@ POST /user/gpg_keys
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| key | string | yes | The new GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | ----------- |
+| key | string | yes | The new GPG key |
```bash
curl --data "key=-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\nxsBNBFV..." --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/user/gpg_keys
@@ -795,9 +853,9 @@ DELETE /user/gpg_keys/:key_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `key_id` | integer | yes | The ID of the GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `key_id` | integer | yes | The ID of the GPG key |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/user/gpg_keys/1
@@ -815,9 +873,9 @@ GET /users/:id/gpg_keys
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the user |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `id` | integer | yes | The ID of the user |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/2/gpg_keys
@@ -845,10 +903,10 @@ GET /users/:id/gpg_keys/:key_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `id` | integer | yes | The ID of the user |
+| `key_id` | integer | yes | The ID of the GPG key |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/2/gpg_keys/1
@@ -874,10 +932,10 @@ POST /users/:id/gpg_keys
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `id` | integer | yes | The ID of the user |
+| `key_id` | integer | yes | The ID of the GPG key |
```bash
curl --data "key=-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\nxsBNBFV..." --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/2/gpg_keys
@@ -905,10 +963,10 @@ DELETE /users/:id/gpg_keys/:key_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ----------- |
+| `id` | integer | yes | The ID of the user |
+| `key_id` | integer | yes | The ID of the GPG key |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/2/gpg_keys/1
@@ -1089,10 +1147,10 @@ GET /users/:user_id/impersonation_tokens
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `user_id` | integer | yes | The ID of the user |
-| `state` | string | no | filter tokens based on state (`all`, `active`, `inactive`) |
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | ---------------------------------------------------------- |
+| `user_id` | integer | yes | The ID of the user |
+| `state` | string | no | filter tokens based on state (`all`, `active`, `inactive`) |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/42/impersonation_tokens
@@ -1141,10 +1199,10 @@ GET /users/:user_id/impersonation_tokens/:impersonation_token_id
Parameters:
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `user_id` | integer | yes | The ID of the user |
-| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
+| Attribute | Type | Required | Description |
+| ------------------------ | ------- | -------- | --------------------------------- |
+| `user_id` | integer | yes | The ID of the user |
+| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/users/42/impersonation_tokens/2
@@ -1170,7 +1228,6 @@ Example response:
## Create an impersonation token
> Requires admin permissions.
-
> Token values are returned once. Make sure you save it - you won't be able to access it again.
It creates a new impersonation token. Note that only administrators can do this.
@@ -1182,12 +1239,12 @@ settings page.
POST /users/:user_id/impersonation_tokens
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `user_id` | integer | yes | The ID of the user |
-| `name` | string | yes | The name of the impersonation token |
-| `expires_at` | date | no | The expiration date of the impersonation token in ISO format (`YYYY-MM-DD`)|
-| `scopes` | array | yes | The array of scopes of the impersonation token (`api`, `read_user`) |
+| Attribute | Type | Required | Description |
+| ------------ | ------- | -------- | ----------- |
+| `user_id` | integer | yes | The ID of the user |
+| `name` | string | yes | The name of the impersonation token |
+| `expires_at` | date | no | The expiration date of the impersonation token in ISO format (`YYYY-MM-DD`)|
+| `scopes` | array | yes | The array of scopes of the impersonation token (`api`, `read_user`) |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --data "name=mytoken" --data "expires_at=2017-04-04" --data "scopes[]=api" https://gitlab.example.com/api/v4/users/42/impersonation_tokens
@@ -1234,15 +1291,15 @@ Parameters:
### Get user activities (admin only)
->**Note:** This API endpoint is only available on 8.15 (EE) and 9.1 (CE) and above.
+NOTE: **Note:** This API endpoint is only available on 8.15 (EE) and 9.1 (CE) and above.
Get the last activity date for all users, sorted from oldest to newest.
The activities that update the timestamp are:
- - Git HTTP/SSH activities (such as clone, push)
- - User logging in into GitLab
- - User visiting pages related to Dashboards, Projects, Issues and Merge Requests ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/54947) in GitLab 11.8)
+- Git HTTP/SSH activities (such as clone, push)
+- User logging in into GitLab
+- User visiting pages related to Dashboards, Projects, Issues and Merge Requests ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/54947) in GitLab 11.8)
By default, it shows the activity for all users in the last 6 months, but this can be
amended by using the `from` parameter.
@@ -1285,4 +1342,4 @@ Example response:
Please note that `last_activity_at` is deprecated, please use `last_activity_on`.
-[gemojione-index]: https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json \ No newline at end of file
+[gemojione-index]: https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 5752fb7c078..5f875528a6c 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -9,7 +9,7 @@ The V3 API documentation is still
Below are the changes made between V3 and V4.
-### 8.17
+## 8.17
- Removed `GET /projects/:search` (use: `GET /projects?search=x`) [!8877](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8877)
- `iid` filter has been removed from `GET /projects/:id/issues` [!8967](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8967)
@@ -18,7 +18,7 @@ Below are the changes made between V3 and V4.
- Project snippets do not return deprecated field `expires_at` [!8723](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8723)
- Endpoints under `GET /projects/:id/keys` have been removed (use `GET /projects/:id/deploy_keys`) [!8716](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8716)
-### 9.0
+## 9.0
- Status 409 returned for `POST /projects/:id/members` when a member already exists [!9093](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9093)
- Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar` [!9328](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9328)
diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md
index 390d0966244..cc6e5d3960b 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -1,4 +1,4 @@
-# Vulnerabilities API **[ULTIMATE]**
+# Vulnerabilities API **(ULTIMATE)**
Every API call to vulnerabilities must be authenticated.
@@ -32,13 +32,13 @@ GET /projects/:id/vulnerabilities?severity=high
GET /projects/:id/vulnerabilities?confidence=unknown,experimental
```
-| Attribute | Type | Required | Description |
-| ------------------- | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `report_type` | Array[string] | no | Returns vulnerabilities belonging to specified report type. Valid values: `sast`, `dast`, `dependency_scanning`, or `container_scanning`. |
-| `scope` | string | no | Returns vulnerabilities for the given scope: `all` or `dismissed`. Defaults to `dismissed` |
-| `severity` | Array[string] | no | Returns vulnerabilities belonging to specified severity level: `undefined`, `info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all' |
-| `confidence` | Array[string] | no | Returns vulnerabilities belonging to specified confidence level: `undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. Defaults to all |
+| Attribute | Type | Required | Description |
+| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `report_type` | string array | no | Returns vulnerabilities belonging to specified report type. Valid values: `sast`, `dast`, `dependency_scanning`, or `container_scanning`. |
+| `scope` | string | no | Returns vulnerabilities for the given scope: `all` or `dismissed`. Defaults to `dismissed` |
+| `severity` | string array | no | Returns vulnerabilities belonging to specified severity level: `undefined`, `info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all' |
+| `confidence` | string array | no | Returns vulnerabilities belonging to specified confidence level: `undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. Defaults to all |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/vulnerabilities
diff --git a/doc/ci/README.md b/doc/ci/README.md
index d851a56ee0e..7048ceaac41 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -104,27 +104,27 @@ Its feature set is listed on the table below according to DevOps stages.
| **Verify** ||
| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the performance impact of pending code changes. |
| [CI services](services/README.md) | Link Docker containers with your base image.|
-| [Code Quality](../user/project/merge_requests/code_quality.md) **[STARTER]** | Analyze your source code quality. |
-| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **[PREMIUM]** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. |
-| [Interactive Web Terminals](interactive_web_terminal/index.md) **[CORE ONLY]** | Open an interactive web terminal to debug the running jobs. |
+| [Code Quality](../user/project/merge_requests/code_quality.md) **(STARTER)** | Analyze your source code quality. |
+| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) **(PREMIUM)** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. |
+| [Interactive Web Terminals](interactive_web_terminal/index.md) **(CORE ONLY)** | Open an interactive web terminal to debug the running jobs. |
| [JUnit tests](junit_test_reports.md) | Identify script failures directly on merge requests. |
| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
|---+---|
| **Release** ||
| [Auto Deploy](../topics/autodevops/index.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
-| [Canary Deployments](../user/project/canary_deployments.md) **[PREMIUM]** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
-| [Deploy Boards](../user/project/deploy_boards.md) **[PREMIUM]** | Check the current health and status of each CI/CD environment running on Kubernetes. |
-| [Feature Flags](../user/project/operations/feature_flags.md) **[PREMIUM]** | Deploy your features behind Feature Flags. |
+| [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
+| [Deploy Boards](../user/project/deploy_boards.md) **(PREMIUM)** | Check the current health and status of each CI/CD environment running on Kubernetes. |
+| [Feature Flags](../user/project/operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
|---+---|
| **Secure** ||
-| [Container Scanning](../user/application_security/container_scanning/index.md) **[ULTIMATE]** | Check your Docker containers for known vulnerabilities.|
-| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. |
-| [License Management](../user/application_security/license_management/index.md) **[ULTIMATE]** | Search your project dependencies for their licenses. |
-| [Security Test reports](../user/project/merge_requests/index.md#security-reports-ultimate) **[ULTIMATE]** | Check for app vulnerabilities. |
+| [Container Scanning](../user/application_security/container_scanning/index.md) **(ULTIMATE)** | Check your Docker containers for known vulnerabilities.|
+| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
+| [License Management](../user/application_security/license_management/index.md) **(ULTIMATE)** | Search your project dependencies for their licenses. |
+| [Security Test reports](../user/project/merge_requests/index.md#security-reports-ultimate) **(ULTIMATE)** | Check for app vulnerabilities. |
## Examples
@@ -133,7 +133,7 @@ on the [CI Examples](examples/README.md) page.
GitLab also provides [example projects](https://gitlab.com/gitlab-examples) pre-configured to use GitLab CI/CD.
-## Administration **[CORE ONLY]**
+## Administration **(CORE ONLY)**
As a GitLab administrator, you can change the default behavior
of GitLab CI/CD for:
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index bbb25c78ec5..b3110b435db 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -2,7 +2,7 @@
type: howto
---
-# Using GitLab CI/CD with a Bitbucket Cloud repository **[PREMIUM]**
+# Using GitLab CI/CD with a Bitbucket Cloud repository **(PREMIUM)**
GitLab CI/CD can be used with Bitbucket Cloud by:
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index 53b36181062..0bb3aa35ed0 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -2,7 +2,7 @@
type: howto
---
-# Using GitLab CI/CD with a GitHub repository **[PREMIUM]**
+# Using GitLab CI/CD with a GitHub repository **(PREMIUM)**
GitLab CI/CD can be used with **GitHub.com** and **GitHub Enterprise** by
creating a [CI/CD project](index.md) to connect your GitHub repository to
@@ -109,7 +109,7 @@ your repository:
new commits.
The web hook URL should be set to the GitLab API to
- [trigger pull mirroring](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter),
+ [trigger pull mirroring](../../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter),
using the GitLab personal access token we just created.
```
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index d46e451c609..02b8eb7daa7 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -2,7 +2,7 @@
type: index, howto
---
-# GitLab CI/CD for external repositories **[PREMIUM]**
+# GitLab CI/CD for external repositories **(PREMIUM)**
>[Introduced][ee-4642] in [GitLab Premium][eep] 10.6.
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index e012f4f8595..2d7fb323d79 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -489,17 +489,17 @@ runtime.
### Using statically-defined credentials
There are two approaches that you can take in order to access a
private registry. Both require setting the environment variable
-`DOCKER_AUTH_LOGIN` with appropriate authentication info.
+`DOCKER_AUTH_CONFIG` with appropriate authentication info.
1. Per-job: To configure one job to access a private registry, add
- `DOCKER_AUTH_LOGIN` as a job variable.
+ `DOCKER_AUTH_CONFIG` as a job variable.
1. Per-runner: To configure a Runner so all its jobs can access a
- private registry, add `DOCKER_AUTH_LOGIN` to the environment in the
+ private registry, add `DOCKER_AUTH_CONFIG` to the environment in the
Runner's configuration.
See below for examples of each.
-#### Determining your `DOCKER_AUTH_LOGIN` data
+#### Determining your `DOCKER_AUTH_CONFIG` data
As an example, let's assume that you want to use the `registry.example.com:5000/private/image:latest`
image which is private and requires you to login into a private container registry.
@@ -530,11 +530,11 @@ There are two ways to determine the value of `DOCKER_AUTH_CONFIG`:
```
- **Second way -** In some setups, it's possible that Docker client
-will use the available system keystore to store the result of `docker
-login`. In that case, it's impossible to read `~/.docker/config.json`,
-so you will need to prepare the required base64-encoded version of
-`${username}:${password}` manually. Open a terminal and execute the
-following command:
+ will use the available system keystore to store the result of `docker
+ login`. In that case, it's impossible to read `~/.docker/config.json`,
+ so you will need to prepare the required base64-encoded version of
+ `${username}:${password}` manually. Open a terminal and execute the
+ following command:
```bash
echo -n "my_username:my_password" | base64
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index a32dbc11a33..f86ca8f74f2 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -673,7 +673,7 @@ fetch line:
fetch = +refs/environments/*:refs/remotes/origin/environments/*
```
-### Scoping environments with specs **[PREMIUM]**
+### Scoping environments with specs **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2112) in [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
@@ -692,7 +692,7 @@ with `review/` would have that particular variable.
Some GitLab features can behave differently for each environment.
For example, you can
-[create a secret variable to be injected only into a production environment](variables/README.md#limiting-environment-scopes-of-environment-variables-premium). **[PREMIUM]**
+[create a secret variable to be injected only into a production environment](variables/README.md#limiting-environment-scopes-of-environment-variables-premium). **(PREMIUM)**
In most cases, these features use the _environment specs_ mechanism, which offers
an efficient way to implement scoping within each environment group.
@@ -734,7 +734,7 @@ Below are some links you may find interesting:
- [The `.gitlab-ci.yml` definition of environments](yaml/README.md#environment)
- [A blog post on Deployments & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
- [Review Apps - Use dynamic environments to deploy your code for every branch](review_apps/index.md)
-- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) **[PREMIUM]**
+- [Deploy Boards for your applications running on Kubernetes](../user/project/deploy_boards.md) **(PREMIUM)**
<!-- ## Troubleshooting
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index b72ebe838b8..e5213881862 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -2,7 +2,7 @@
type: concepts, howto
---
-# Protected Environments **[PREMIUM]**
+# Protected Environments **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6303) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.3.
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 2b4fe321cb3..5a302392c54 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -42,10 +42,10 @@ The following table lists examples with step-by-step tutorials that are containe
Contributions are welcome! You can help your favorite programming
language users and GitLab by sending a merge request with a guide for that language.
-You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
+You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community/writers/)
to get paid for writing complete articles for GitLab.
-## Adding templates to your GitLab installation **[PREMIUM ONLY]**
+## Adding templates to your GitLab installation **(PREMIUM ONLY)**
If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](../../user/admin_area/settings/instance_template_repository.md) that contains examples and templates specific to your enterprise.
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 2117b342903..c9f700ed190 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -13,7 +13,7 @@ date: 2017-08-15
## Introduction
In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/)
-to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://www.jfrog.com/artifactory/), and then use it from another Maven application as a dependency.
+to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://jfrog.com/artifactory/), and then use it from another Maven application as a dependency.
You'll create two different projects:
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 7f1beb96bbf..38fcf05f519 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -32,7 +32,7 @@ through the process of setting up GitLab CI/CD for end-to-end testing Javascript
with WebdriverIO, but the general strategy should carry over to other languages.
We assume you are familiar with GitLab, [GitLab CI/CD](../../README.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`.
-### What to test
+## What to test
In the widely-used [testing pyramid strategy](https://martinfowler.com/bliki/TestPyramid.html), end-to-end tests act more like a
safeguard: [most of your code should be covered by
@@ -40,9 +40,9 @@ unit tests](https://vincenttunru.com/100-percent-coverage/) that allow you to ea
will likely want to
[limit the number of end-to-end tests](https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html)
to just enough to give you the confidence that the deployment went as intended, that your
-infrastructure is up and running, and that your units of code work well together.
+infrastructure is up and running, and that your units of code work well together.
-### Selenium and WebdriverIO
+## Selenium and WebdriverIO
[Selenium](http://www.seleniumhq.org/) is a piece of software that can control web browsers, e.g., to make them
visit a specific URL or interact with elements on the page. It can be programmatically controlled
@@ -65,7 +65,7 @@ describe('A visitor without account', function(){
expect(browser.getUrl()).toMatch('page-that-does-not-exist');
browser.element('.content a[href="/"]').click();
-
+
expect(browser.getUrl()).not.toMatch('page-that-does-not-exist');
});
});
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index c459bb7001f..0b9e9e93e55 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -64,7 +64,7 @@ docker-php-ext-install pdo_mysql
You might wonder what `docker-php-ext-install` is. In short, it is a script
provided by the official php docker image that you can use to easily install
extensions. For more information read the documentation at
-<https://hub.docker.com/r/_/php/>.
+<https://hub.docker.com/_/php>.
Now that we created the script that contains all prerequisites for our build
environment, let's add it in `.gitlab-ci.yml`:
@@ -96,7 +96,7 @@ Finally, commit your files and push them to GitLab to see your build succeeding
The final `.gitlab-ci.yml` should look similar to this:
```yaml
-# Select image from https://hub.docker.com/r/_/php/
+# Select image from https://hub.docker.com/_/php
image: php:5.6
before_script:
@@ -286,7 +286,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available
Want to hack on it? Simply fork it, commit, and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
-[php-hub]: https://hub.docker.com/r/_/php/
+[php-hub]: https://hub.docker.com/_/php
[phpenv]: https://github.com/phpenv/phpenv
[phpenv-installation]: https://github.com/phpenv/phpenv#installation
[php-example-repo]: https://gitlab.com/gitlab-examples/php
diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md
index 5cda8702b56..6ea38f22bca 100644
--- a/doc/ci/examples/test-clojure-application.md
+++ b/doc/ci/examples/test-clojure-application.md
@@ -35,7 +35,7 @@ test:
- lein test
```
-In `before_script`, we install JRE and [Leiningen](http://leiningen.org/).
+In `before_script`, we install JRE and [Leiningen](https://leiningen.org/).
The sample project uses the [migratus](https://github.com/yogthos/migratus) library to manage database migrations, and
we have added a database migration as the last step of `before_script`.
diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md
index bd899240307..7d039ab1aeb 100644
--- a/doc/ci/examples/test-scala-application.md
+++ b/doc/ci/examples/test-scala-application.md
@@ -46,7 +46,7 @@ deploy:
In the above configuration:
-- The `before_script` installs [SBT](http://www.scala-sbt.org/) and
+- The `before_script` installs [SBT](https://www.scala-sbt.org/) and
displays the version that is being used.
- The `test` stage executes SBT to compile and test the project.
- [sbt-scoverage](https://github.com/scoverage/sbt-scoverage) is used as an SBT
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index ec25ca1bfc3..a5fed00972f 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -404,14 +404,14 @@ other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams
[phoenix-learning-guide]: https://hexdocs.pm/phoenix/learning.html "Phoenix Learning Guide"
[phoenix-install]: https://hexdocs.pm/phoenix/installation.html "Phoenix Installation"
[phoenix-mysql]: https://hexdocs.pm/phoenix/ecto.html#using-mysql "Phoenix with MySQL"
-[elixir-site]: http://elixir-lang.org/ "Elixir"
-[elixir-mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix"
-[elixir-docs]: http://elixir-lang.org/getting-started/introduction.html "Elixir Documentation"
+[elixir-site]: https://elixir-lang.org/ "Elixir"
+[elixir-mix]: https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix"
+[elixir-docs]: https://elixir-lang.org/getting-started/introduction.html "Elixir Documentation"
[elixir-install]: https://elixir-lang.org/install.html "Elixir Installation"
-[ecto]: http://hexdocs.pm/ecto "Ecto"
+[ecto]: https://hexdocs.pm/ecto/Ecto.html "Ecto"
[ecto-repo]: https://hexdocs.pm/ecto/Ecto.html#module-repositories "Ecto Repositories"
[mix-ecto]: https://hexdocs.pm/ecto/Mix.Tasks.Ecto.Create.html "mix and Ecto"
-[iex]: http://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode"
+[iex]: https://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode"
[ci-lint]: https://gitlab.com/ci/lint "CI Lint Tool"
[ci-reasons]: https://about.gitlab.com/2015/02/03/7-reasons-why-you-should-be-using-ci/ "7 Reasons Why You Should Be Using CI"
[ci-guide]: https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/ "Getting Started With GitLab And GitLab CI/CD"
diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md
index 551044dd76f..1354a26d6e2 100644
--- a/doc/ci/git_submodules.md
+++ b/doc/ci/git_submodules.md
@@ -79,14 +79,14 @@ correctly with your CI jobs:
1. If you are using an older version of `gitlab-runner`, then use
`git submodule sync/update` in `before_script`:
- ```yaml
- before_script:
- - git submodule sync --recursive
- - git submodule update --init --recursive
- ```
-
- `--recursive` should be used in either both or none (`sync/update`) depending on
- whether you have recursive submodules.
+ ```yaml
+ before_script:
+ - git submodule sync --recursive
+ - git submodule update --init --recursive
+ ```
+
+ `--recursive` should be used in either both or none (`sync/update`) depending on
+ whether you have recursive submodules.
The rationale to set the `sync` and `update` in `before_script` is because of
the way Git submodules work. On a fresh Runner workspace, Git will set the
diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md
index 1387d4df500..6c73cbbf8d7 100644
--- a/doc/ci/interactive_web_terminal/index.md
+++ b/doc/ci/interactive_web_terminal/index.md
@@ -59,7 +59,7 @@ close the terminal window.
![finished job with terminal open](img/finished_job_with_terminal_open.png)
-## Interactive Web Terminals for the Web IDE **[ULTIMATE ONLY]**
+## Interactive Web Terminals for the Web IDE **(ULTIMATE ONLY)**
Read the Web IDE docs to learn how to run [Interactive Terminals through the Web IDE](../../user/project/web_ide/index.md).
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
index ef9f9a9973c..fc89f0fc94f 100644
--- a/doc/ci/introduction/index.md
+++ b/doc/ci/introduction/index.md
@@ -177,22 +177,22 @@ according to each stage (Verify, Package, Release).
1. **Verify**:
- Automatically build and test your application with Continuous Integration.
- - Analyze your source code quality with [GitLab Code Quality](../../user/project/merge_requests/code_quality.md). **[STARTER]**
- - Determine the performance impact of code changes with [Browser Performance Testing](../../user/project/merge_requests/browser_performance_testing.md). **[PREMIUM]**
- - Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **[ULTIMATE]**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **[ULTIMATE]**, and [JUnit tests](../junit_test_reports.md).
+ - Analyze your source code quality with [GitLab Code Quality](../../user/project/merge_requests/code_quality.md). **(STARTER)**
+ - Determine the performance impact of code changes with [Browser Performance Testing](../../user/project/merge_requests/browser_performance_testing.md). **(PREMIUM)**
+ - Perform a series of tests, such as [Container Scanning](../../user/application_security/container_scanning/index.md) **(ULTIMATE)**, [Dependency Scanning](../../user/application_security/dependency_scanning/index.md) **(ULTIMATE)**, and [JUnit tests](../junit_test_reports.md).
- Deploy your changes with [Review Apps](../review_apps/index.md) to preview the app changes on every branch.
1. **Package**:
- Store Docker images with [Container Registry](../../user/project/container_registry.md).
- - Store NPM packages with [NPM Registry](../../user/project/packages/npm_registry.md). **[PREMIUM]**
- - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **[PREMIUM]**
+ - Store NPM packages with [NPM Registry](../../user/project/packages/npm_registry.md). **(PREMIUM)**
+ - Store Maven artifacts with [Maven Repository](../../user/project/packages/maven_repository.md). **(PREMIUM)**
1. **Release**:
- Continuous Deployment, automatically deploying your app to production.
- Continuous Delivery, manually click to deploy your app to production.
- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md).
- - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **[PREMIUM]**
- - Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **[PREMIUM]**
+ - Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **(PREMIUM)**
+ - Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **(PREMIUM)**
- Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md).
- - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **[PREMIUM]**
+ - View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **(PREMIUM)**
- Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/index.md#auto-deploy).
With GitLab CI/CD you can also:
@@ -201,7 +201,7 @@ With GitLab CI/CD you can also:
- Deploy your app to different [environments](../environments.md).
- Install your own [GitLab Runner](https://docs.gitlab.com/runner/).
- [Schedule pipelines](../../user/project/pipelines/schedules.md).
-- Check for app vulnerabilities with [Security Test reports](../../user/project/merge_requests/index.md#security-reports-ultimate). **[ULTIMATE]**
+- Check for app vulnerabilities with [Security Test reports](../../user/project/merge_requests/index.md#security-reports-ultimate). **(ULTIMATE)**
To see all CI/CD features, navigate back to the [CI/CD index](../README.md).
diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md
index e70ae0bd154..72a9a876037 100644
--- a/doc/ci/merge_request_pipelines/index.md
+++ b/doc/ci/merge_request_pipelines/index.md
@@ -76,11 +76,11 @@ when a merge request was created or updated. For example:
![Merge request page](img/merge_request.png)
-## Pipelines for Merged Results **[PREMIUM]**
+## Pipelines for Merged Results **(PREMIUM)**
Read the [documentation on Pipelines for Merged Results](pipelines_for_merged_results/index.md).
-### Merge Trains **[PREMIUM]**
+### Merge Trains **(PREMIUM)**
Read the [documentation on Merge Trains](pipelines_for_merged_results/merge_trains/index.md).
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 3c5088089fa..a13857bee25 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
@@ -3,7 +3,7 @@ type: reference
last_update: 2019-07-03
---
-# Pipelines for Merged Results **[PREMIUM]**
+# Pipelines for Merged Results **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
> This feature is disabled by default until we resolve issues with [contention handling](https://gitlab.com/gitlab-org/gitlab-ee/issues/11222), but [can be enabled manually](#enabling-pipelines-for-merged-results).
@@ -61,7 +61,7 @@ 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]**
+## Merge Trains **(PREMIUM)**
Read the [documentation on Merge Trains](merge_trains/index.md).
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 c5ff6f9ebed..57358434c02 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
@@ -3,7 +3,7 @@ type: reference
last_update: 2019-07-03
---
-# Merge Trains **[PREMIUM]**
+# Merge Trains **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9186) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.0.
> This feature is disabled by default, but [can be enabled manually](#enabling-merge-trains).
diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md
index f9cfc0892a7..4d3f5a143f7 100644
--- a/doc/ci/metrics_reports.md
+++ b/doc/ci/metrics_reports.md
@@ -2,9 +2,9 @@
type: reference
---
-# Metrics Reports **[PREMIUM]**
+# Metrics Reports **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9788) in [GitLab Premium](https://about.gitlab.com/pricing) 11.10.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9788) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
Requires GitLab Runner 11.10 and above.
## Overview
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
index 50c8d82602b..463b9194c58 100644
--- a/doc/ci/multi_project_pipelines.md
+++ b/doc/ci/multi_project_pipelines.md
@@ -2,7 +2,7 @@
type: reference
---
-# Multi-project pipelines **[PREMIUM]**
+# Multi-project pipelines **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/2121) in
[GitLab Premium 9.3](https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#multi-project-pipeline-graphs).
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index 4a07aa31f8a..06a81c3d0c7 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -234,7 +234,7 @@ Pipeline status and test coverage report badges are available and configurable f
For information on adding pipeline badges to projects, see [Pipeline badges](../user/project/pipelines/settings.md#pipeline-badges).
-## Multi-project pipelines **[PREMIUM]**
+## Multi-project pipelines **(PREMIUM)**
Pipelines for different projects can be combined and visualized together.
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 11bcfd5dc2c..0480b83d183 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -233,7 +233,7 @@ CI with various languages.
[runner-install]: https://docs.gitlab.com/runner/install/
[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/
[examples]: ../examples/README.md
-[ci]: https://about.gitlab.com/gitlab-ci/
+[ci]: https://about.gitlab.com/product/continuous-integration/
[yaml]: ../yaml/README.md
[runner]: ../runners/README.md
[enabled]: ../enable_or_disable_ci.md
diff --git a/doc/ci/review_apps/img/toolbar_feeback_form.png b/doc/ci/review_apps/img/toolbar_feeback_form.png
index d147981a387..fe1c7e6e611 100644
--- a/doc/ci/review_apps/img/toolbar_feeback_form.png
+++ b/doc/ci/review_apps/img/toolbar_feeback_form.png
Binary files differ
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index 70934e074a0..9b89988bf42 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -27,7 +27,7 @@ In the above example:
- Once the review as passed, `topic branch` is merged into `master` where it's deploy to staging.
- After been approved in staging, the changes that were merged into `master` are deployed in to production.
-### How Review Apps work
+## How Review Apps work
A Review App is a mapping of a branch with an [environment](../environments.md).
Access to the Review App is made available as a link on the [merge request](../../user/project/merge_requests.md) relevant to the branch.
@@ -41,27 +41,34 @@ In this example, a branch was:
- Successfully built.
- Deployed under a dynamic environment that can be reached by clicking on the **View app** button.
+After adding Review Apps to your workflow, you follow the branched Git flow. That is:
+
+1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job.
+1. Wait for the Runner to build and deploy your web application.
+1. Click on the link provided in the merge request related to the branch to see the changes live.
+
## Configuring Review Apps
Review Apps are built on [dynamic environments](../environments.md#configuring-dynamic-environments), which allow you to dynamically create a new environment for each branch.
The process of configuring Review Apps is as follows:
-1. Set up the infrastructure to host and deploy the Review Apps.
+1. Set up the infrastructure to host and deploy the Review Apps (check the [examples](#review-apps-examples) below).
1. [Install](https://docs.gitlab.com/runner/install/) and [configure](https://docs.gitlab.com/runner/commands/) a Runner to do deployment.
1. Set up a job in `.gitlab-ci.yml` that uses the [predefined CI environment variable](../variables/README.md) `${CI_COMMIT_REF_NAME}` to create dynamic environments and restrict it to run only on branches.
1. Optionally, set a job that [manually stops](../environments.md#stopping-an-environment) the Review Apps.
-### Examples
+## Review Apps examples
The following are example projects that demonstrate Review App configuration:
- [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx).
- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift).
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example.
-### Route Maps
+## Route Maps
> Introduced in GitLab 8.17. In GitLab 11.5, the file links are available in the merge request widget.
@@ -82,7 +89,7 @@ To set up a route map, add a a file inside the repository at `.gitlab/route-map.
which contains a YAML array that maps `source` paths (in the repository) to `public`
paths (on the website).
-#### Route Maps example
+### Route Maps example
The following is an example of a route map for [Middleman](https://middlemanapp.com),
a static site generator (SSG) used to build [GitLab's website](https://about.gitlab.com),
@@ -146,51 +153,102 @@ Once you have the route mapping set up, it will take effect in the following loc
!["View on env" button in file view](img/view_on_env_blob.png)
-## Working with Review Apps
-
-After adding Review Apps to your workflow, you follow the branched Git flow. That is:
-
-1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job.
-1. Wait for the Runner to build and deploy your web application.
-1. Click on the link that provided in the merge request related to the branch to see the changes live.
-
-### Visual Reviews **[STARTER]**
+## Visual Reviews **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/10761) in GitLab Starter 12.0.
-The Visual Reviews feedback form can be added to a Review App to enable reviewers to post comments
-directly from the app back to the merge request that spawned the Review App.
+With Visual Reviews, you can provide a feedback form to your Review Apps so
+that reviewers can post comments directly from the app back to the merge request
+that spawned the Review App.
-For example, a form like the following can be configured to post the contents of the
-text field into the discussion thread of a merge request:
+### Configuring Visual Reviews
-![feedback form](img/toolbar_feeback_form.png)
+The feedback form is served through a script you add to pages in your Review App.
+If you have [Developer permissions](../../user/permissions.md) to the project,
+you can access it by clicking the **Review** button in the **Pipeline** section
+of the merge request.
-#### Using Visual Reviews
+![review button](img/review_button.png)
-If Visual Reviews has been [enabled](#configuring-visual-reviews) for the Review App, the Visual Reviews feedback form is overlaid on the app's pages at the bottom-right corner.
+The provided script should be added to the `<head>` of you application and
+consists of some project and merge request specific values. Here's what it
+looks like:
+
+```html
+<script
+ data-project-id='11790219'
+ data-merge-request-id='1'
+ data-mr-url='https://gitlab.example.com'
+ data-project-path='sarah/review-app-tester'
+ id='review-app-toolbar-script'
+ src='https://gitlab.example.com/assets/webpack/visual_review_toolbar.js'>
+</script>
+```
-To use the feedback form, you will need to create a [personal access token](../../user/profile/personal_access_tokens.md) with the API scope selected.
+Ideally, you should use [environment variables](../variables/predefined_variables.md)
+to replace those values at runtime when each review app is created:
+
+- `data-project-id` is the project ID, which can be found by the `CI_PROJECT_ID`
+ variable.
+- `data-merge-request-id` is the merge request ID, which can be found by the
+ `CI_MERGE_REQUEST_IID` variable. `CI_MERGE_REQUEST_IID` is available only if
+ [`only: [merge_requests]`](../merge_request_pipelines/index.md)
+ is used and the merge request is created.
+- `data-mr-url` is the URL of the GitLab instance and will be the same for all
+ review apps.
+- `data-project-path` is the project's path, which can be found by `CI_PROJECT_PATH`.
+- `id` is always `review-app-toolbar-script`, you don't need to change that.
+- `src` is the source of the review toolbar script, which resides in the
+ respective GitLab instance and will be the same for all review apps.
+
+For example, in a Ruby application, you would need to have this script:
+
+```html
+<script
+ data-project-id="ENV['CI_PROJECT_ID']"
+ data-merge-request-id="ENV['CI_MERGE_REQUEST_IID']"
+ data-mr-url='https://gitlab.example.com'
+ data-project-path="ENV['CI_PROJECT_PATH']"
+ id='review-app-toolbar-script'
+ src='https://gitlab.example.com/assets/webpack/visual_review_toolbar.js'>
+</script>
+```
-Paste the token into the feedback box, when prompted. If you select **Remember me**, your browser stores the token so that future visits to Review Apps at the same URL will not require you to re-enter the token. To clear the token, click **Log out**.
+Then, when your app is deployed via GitLab CI/CD, those variables should get
+replaced with their real values.
-Because tokens must be entered on a per-domain basis and they can only be accessed once, you can save the token to your password manager specifically for the purpose of Visual Reviews. This way, you will not need to create additional tokens for each merge request.
+NOTE: **Note:**
+Future enhancements [are planned](https://gitlab.com/gitlab-org/gitlab-ee/issues/11322)
+to make this process even easier.
-Comments can make use of all the [Markdown annotations](../../user/markdown.md)
-available in merge request comment boxes.
+### Using Visual Reviews
-#### Configuring Visual Reviews
+After Visual Reviews has been [enabled](#configuring-visual-reviews) for the
+Review App, the Visual Reviews feedback form is overlaid on the app's pages at
+the bottom-right corner.
-The feedback form is served through a script you add to pages in your Review App.
-To access the code to include the script, click the **Review** button in the **Pipeline** section of the merge request.
+![Visual review feedback form](img/toolbar_feeback_form.png)
-![review button](img/review_button.png)
+To use the feedback form:
+
+1. Create a [personal access token](../../user/profile/personal_access_tokens.md)
+ with the API scope selected.
+1. Paste the token into the feedback box when prompted. If you select **Remember me**,
+ your browser stores the token so that future visits to Review Apps at the same URL
+ will not require you to re-enter the token. To clear the token, click **Log out**.
+1. Make a comment on the visual review. You can make use of all the
+ [Markdown annotations](../../user/markdown.md) that are also available in
+ merge request comments.
+1. Finally, click **Send feedback**.
-The provided script hardcodes the project and merge request IDs. You may want to consider
-using features of your programming language to use environment variables or other
-means to inject these at runtime.
+After you make and submit a comment in the visual review box, it will appear
+automatically in the respective merge request.
-Future enhancements [are planned](https://gitlab.com/gitlab-org/gitlab-ee/issues/11322) to make this process even easier.
+TIP: **Tip:**
+Because tokens must be entered on a per-domain basis and they can only be accessed
+once, different review apps will not remember your token. You can save the token
+to your password manager specifically for the purpose of Visual Reviews. This way,
+you will not need to create additional tokens for each merge request.
## Limitations
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
index 211eea26eb0..960346ac11a 100644
--- a/doc/ci/services/postgres.md
+++ b/doc/ci/services/postgres.md
@@ -114,5 +114,5 @@ available [shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
-[hub-pg]: https://hub.docker.com/r/_/postgres/
+[hub-pg]: https://hub.docker.com/_/postgres
[postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 04c541fefe7..d1f9aa03b6b 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -32,7 +32,7 @@ to protect trigger tokens.
You can use the `CI_JOB_TOKEN` [variable][predef] (used to authenticate
with the [GitLab Container Registry][registry]) in the following cases.
-#### When used with multi-project pipelines **[PREMIUM]**
+#### When used with multi-project pipelines **(PREMIUM)**
> **Note**:
The use of `CI_JOB_TOKEN` for multi-project pipelines was [introduced][ee-2017]
@@ -56,7 +56,7 @@ Pipelines triggered that way also expose a special variable:
Read more about the [pipelines trigger API][trigapi].
-#### When a pipeline depends on the artifacts of another pipeline **[PREMIUM]**
+#### When a pipeline depends on the artifacts of another pipeline **(PREMIUM)**
> The use of `CI_JOB_TOKEN` in the artifacts download API was [introduced][ee-2346]
in [GitLab Premium][ee] 9.5.
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 1b50273eca2..42dd4f08ed8 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -389,7 +389,7 @@ Protected variables can be added by going to your project's
Once you set them, they will be available for all subsequent pipelines.
-### Limiting environment scopes of environment variables **[PREMIUM]**
+### Limiting environment scopes of environment variables **(PREMIUM)**
You can limit the environment scope of a variable by
[defining which environments][envs] it can be available for.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 2759f1c5160..474db05de06 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -973,6 +973,8 @@ review_app:
stop_review_app:
stage: deploy
+ variables:
+ GIT_STRATEGY: none
script: make delete-app
when: manual
environment:
@@ -987,6 +989,10 @@ Once the `review_app` job is successfully finished, it will trigger the
set it up to `manual` so it will need a [manual action](#whenmanual) via
GitLab's web interface in order to run.
+Also in the example, `GIT_STRATEGY` is set to `none` so that GitLab Runner won’t
+try to check out the code after the branch is deleted when the `stop_review_app`
+job is [automatically triggered](../environments.md#automatically-stopping-an-environment).
+
The `stop_review_app` job is **required** to have the following keywords defined:
- `when` - [reference](#when)
@@ -1051,7 +1057,7 @@ globally and all jobs will use that definition.
#### `cache:paths`
Use the `paths` directive to choose which files or directories will be cached.
-Wildcards can be used as well.
+Wildcards can be used that follow the [glob](https://en.wikipedia.org/wiki/Glob_(programming)) patterns and [filepath.Match](https://golang.org/pkg/path/filepath/#Match).
Cache all files in `binaries` that end in `.apk` and the `.config` file:
@@ -1213,8 +1219,10 @@ be available for download in the GitLab UI.
#### `artifacts:paths`
-You can only use paths that are within the project workspace. To pass artifacts
-between different jobs, see [dependencies](#dependencies).
+You can only use paths that are within the project workspace.
+Wildcards can be used that follow the [glob](https://en.wikipedia.org/wiki/Glob_(programming)) patterns and [filepath.Match](https://golang.org/pkg/path/filepath/#Match).
+
+To pass artifacts between different jobs, see [dependencies](#dependencies).
Send all files in `binaries` and `.config`:
@@ -1469,7 +1477,7 @@ concatenated into a single file. Use a filename pattern (`junit: rspec-*.xml`),
an array of filenames (`junit: [rspec-1.xml, rspec-2.xml, rspec-3.xml]`), or a
combination thereof (`junit: [rspec.xml, test-results/TEST-*.xml]`).
-##### `artifacts:reports:codequality` **[STARTER]**
+##### `artifacts:reports:codequality` **(STARTER)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1479,7 +1487,7 @@ as artifacts.
The collected Code Quality report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests.
-##### `artifacts:reports:sast` **[ULTIMATE]**
+##### `artifacts:reports:sast` **(ULTIMATE)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1490,7 +1498,7 @@ The collected SAST report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-##### `artifacts:reports:dependency_scanning` **[ULTIMATE]**
+##### `artifacts:reports:dependency_scanning` **(ULTIMATE)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1501,7 +1509,7 @@ The collected Dependency Scanning report will be uploaded to GitLab as an artifa
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-##### `artifacts:reports:container_scanning` **[ULTIMATE]**
+##### `artifacts:reports:container_scanning` **(ULTIMATE)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1512,7 +1520,7 @@ The collected Container Scanning report will be uploaded to GitLab as an artifac
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-##### `artifacts:reports:dast` **[ULTIMATE]**
+##### `artifacts:reports:dast` **(ULTIMATE)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1523,7 +1531,7 @@ The collected DAST report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-##### `artifacts:reports:license_management` **[ULTIMATE]**
+##### `artifacts:reports:license_management` **(ULTIMATE)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1534,7 +1542,7 @@ The collected License Management report will be uploaded to GitLab as an artifac
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-##### `artifacts:reports:performance` **[PREMIUM]**
+##### `artifacts:reports:performance` **(PREMIUM)**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1544,7 +1552,7 @@ as artifacts.
The collected Performance report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests.
-##### `artifacts:reports:metrics` **[PREMIUM]**
+##### `artifacts:reports:metrics` **(PREMIUM)**
> Introduced in GitLab 11.10.
@@ -1741,7 +1749,7 @@ test:
parallel: 5
```
-### `trigger` **[PREMIUM]**
+### `trigger` **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.
diff --git a/doc/customization/index.md b/doc/customization/index.md
index 71e87b3f111..0198059297f 100644
--- a/doc/customization/index.md
+++ b/doc/customization/index.md
@@ -2,7 +2,7 @@
description: Learn how to customize GitLab's appearance for self-managed installations.
---
-# Customizing GitLab's appearance **[CORE ONLY]**
+# Customizing GitLab's appearance **(CORE ONLY)**
For GitLab self-managed instances, it's possible to customize
a few pages.
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 680c51e7524..9333f55ca9c 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -1,3 +1,5 @@
---
-redirect_to: '../user/project/issues/automatic_issue_closing.md'
+redirect_to: '../user/project/issues/managing_issues.md#closing-issues-automatically'
---
+
+This document was moved to [another location](../user/project/issues/managing_issues.md#closing-issues-automatically).
diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md
index 18aaeb5a712..e618f3be2fe 100644
--- a/doc/customization/libravatar.md
+++ b/doc/customization/libravatar.md
@@ -6,12 +6,12 @@ Libravatar is a service which delivers your avatar (profile picture) to other we
This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server.
-# Configuration
+## Configuration
In [gitlab.yml gravatar section](https://gitlab.com/gitlab-org/gitlab-ce/blob/672bd3902d86b78d730cea809fce312ec49d39d7/config/gitlab.yml.example#L122) set
the configuration options as follows:
-## For HTTP
+### For HTTP
```yml
gravatar:
@@ -20,7 +20,7 @@ the configuration options as follows:
plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
-## For HTTPS
+### For HTTPS
```yml
gravatar:
@@ -29,7 +29,7 @@ the configuration options as follows:
ssl_url: "https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon"
```
-## Self-hosted
+### Self-hosted
If you are [running your own libravatar service](https://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration
but the important part is to provide the same placeholders so GitLab can parse the URL correctly.
@@ -38,7 +38,7 @@ For example, you host a service on `http://libravatar.example.com` the `plain_ur
`http://libravatar.example.com/avatar/%{hash}?s=%{size}&d=identicon`
-## Omnibus-gitlab example
+### Omnibus-gitlab example
In `/etc/gitlab/gitlab.rb`:
@@ -67,7 +67,7 @@ For example, you can use `retro` set in which case the URL would look like: `pla
## Usage examples
-#### For Microsoft Office 365
+### For Microsoft Office 365
If your users are Office 365-users, the "GetPersonaPhoto" service can be used. Note that this service requires login, so this use case is
most useful in a corporate installation, where all users have access to Office 365.
diff --git a/doc/customization/system_header_and_footer_messages.md b/doc/customization/system_header_and_footer_messages.md
index 7eee79abc77..15830be4e8a 100644
--- a/doc/customization/system_header_and_footer_messages.md
+++ b/doc/customization/system_header_and_footer_messages.md
@@ -8,8 +8,8 @@ Navigate to the **Admin** area and go to the **Appearance** page.
Under **System header and footer** insert your header message and/or footer message.
Both background and font color of the header and footer are customizable.
-You can also apply the header and footer messages to gitlab emails,
-by checking the **Enable header and footer in emails** checkbox.
+You can also apply the header and footer messages to gitlab emails,
+by checking the **Enable header and footer in emails** checkbox.
Note that color settings will only be applied within the app interface and not to emails
![appearance](system_header_and_footer_messages/appearance.png)
diff --git a/doc/development/README.md b/doc/development/README.md
index 5df6ec5fd56..1566173992a 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -82,6 +82,7 @@ description: 'Learn how to contribute to GitLab.'
- [Understanding EXPLAIN plans](understanding_explain_plans.md)
- [explain.depesz.com](https://explain.depesz.com/) for visualising the output
of `EXPLAIN`
+- [pgFormatter](http://sqlformat.darold.net/) a PostgreSQL SQL syntax beautifier
### Migrations
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index aeddad14995..c83a0427c98 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -2,13 +2,14 @@
## Deep Dive
-In March 2019, Nick Thomas hosted a [Deep Dive] on GitLab's [GraphQL API] to share his domain specific knowledge with anyone who may work in this part of the code base in the future. You can find the [recording on YouTube], and the slides on [Google Slides] and in [PDF]. Everything covered in this deep dive was accurate as of GitLab 11.9, and while specific details may have changed since then, it should still serve as a good introduction.
-
-[Deep Dive]: https://gitlab.com/gitlab-org/create-stage/issues/1
-[Pull Repository Mirroring functionality]: ../api/graphql/
-[recording on YouTube]: https://www.youtube.com/watch?v=-9L_1MWrjkg
-[Google Slides]: https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit
-[PDF]: https://gitlab.com/gitlab-org/create-stage/uploads/8e78ea7f326b2ef649e7d7d569c26d56/GraphQL_Deep_Dive__Create_.pdf
+In March 2019, Nick Thomas hosted a [Deep Dive](https://gitlab.com/gitlab-org/create-stage/issues/1)
+on GitLab's [GraphQL API](../api/graphql/index.md) to share his domain specific knowledge
+with anyone who may work in this part of the code base in the future. You can find the
+[recording on YouTube](https://www.youtube.com/watch?v=-9L_1MWrjkg), and the slides on
+[Google Slides](https://docs.google.com/presentation/d/1qOTxpkTdHIp1CRjuTvO-aXg0_rUtzE3ETfLUdnBB5uQ/edit)
+and in [PDF](https://gitlab.com/gitlab-org/create-stage/uploads/8e78ea7f326b2ef649e7d7d569c26d56/GraphQL_Deep_Dive__Create_.pdf).
+Everything covered in this deep dive was accurate as of GitLab 11.9, and while specific
+details may have changed since then, it should still serve as a good introduction.
## Authentication
diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md
index 4fc38a460f8..0866d3baeeb 100644
--- a/doc/development/api_styleguide.md
+++ b/doc/development/api_styleguide.md
@@ -53,7 +53,7 @@ allowed.
### Exclude params from parent namespaces!
-> By default `declared(params) `includes parameters that were defined in all
+> By default `declared(params)`includes parameters that were defined in all
parent namespaces.
– <https://github.com/ruby-grape/grape#include-parent-namespaces>
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index ee6d00331e3..b645a72567c 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -267,7 +267,7 @@ GitLab CI is the open-source continuous integration service included with GitLab
#### Gitlab Workhorse
- [Project page](https://gitlab.com/gitlab-org/gitlab-workhorse/blob/master/README.md)
-- Configuration: [Omnibus][gitlab-workhorse-omnibus], [Charts][gitlab-workhorse-charts], [Source][workhorse-source]
+- Configuration: [Omnibus][workhorse-omnibus], [Charts][workhorse-charts], [Source][workhorse-source]
- Layer: Core Service (Processor)
- Process: `gitlab-workhorse`
@@ -484,9 +484,11 @@ When making a request to an HTTP Endpoint (think `/users/sign_in`) the request w
Below we describe the different pathing that HTTP vs. SSH Git requests will take. There is some overlap with the Web Request Cycle but also some differences.
### Web Request (80/443)
+
TODO
### SSH Request (22)
+
TODO
## System Layout
@@ -505,7 +507,9 @@ To summarize here's the [directory structure of the `git` user home directory](.
### Processes
- ps aux | grep '^git'
+```sh
+ps aux | grep '^git'
+```
GitLab has several components to operate. As a system user (i.e. any user that is not the `git` user) it requires a persistent database (MySQL/PostreSQL) and redis database. It also uses Apache httpd or Nginx to proxypass Unicorn. As the `git` user it starts Sidekiq and Unicorn (a simple ruby HTTP server running on port `8080` by default). Under the GitLab user there are normally 4 processes: `unicorn_rails master` (1 process), `unicorn_rails worker` (2 processes), `sidekiq` (1 process).
@@ -681,7 +685,7 @@ We've also detailed [our architecture of GitLab.com](https://about.gitlab.com/ha
[pgbouncer-exporter-omnibus]: ../administration/monitoring/prometheus/pgbouncer_exporter.md
[pgbouncer-exporter-charts]: https://docs.gitlab.com/charts/installation/deployment.html#postgresql
[gitlab-monitor-omnibus]: ../administration/monitoring/prometheus/gitlab_monitor_exporter.md
-[gitab-monitor-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitlab-monitor/index.html
+[gitlab-monitor-charts]: https://docs.gitlab.com/charts/charts/gitlab/gitlab-monitor/index.html
[node-exporter-omnibus]: ../administration/monitoring/prometheus/node_exporter.md
[node-exporter-charts]: https://gitlab.com/charts/gitlab/issues/1332
[mattermost-omnibus]: https://docs.gitlab.com/omnibus/gitlab-mattermost/
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index 3ca841353e6..423b35a9e3a 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -115,7 +115,7 @@ Now, every time you create an MR for CE and EE:
1. Continue cherry-picking: `git cherry-pick --continue`
1. Push to EE: `git push origin branch-example-ee`
1. Create the EE-equivalent MR and link to the CE MR from the
- description "Ports [CE-MR-LINK] to EE"
+ description `Ports [CE-MR-LINK] to EE`
1. Once all the jobs are passing in both CE and EE, you've addressed the
feedback from your own team, and got them approved, the merge requests can be merged.
1. When both MRs are ready, the EE merge request will be merged first, and the
@@ -125,42 +125,43 @@ Now, every time you create an MR for CE and EE:
- The commit SHA can be easily found from the GitLab UI. From a merge request,
open the tab **Commits** and click the copy icon to copy the commit SHA.
-- To cherry-pick a **commit range**, such as [A > B > C > D] use:
+- To cherry-pick a **commit range**, such as (A > B > C > D) use:
- ```shell
- git cherry-pick "oldest-commit-SHA^..newest-commit-SHA"
- ```
+ ```shell
+ git cherry-pick "oldest-commit-SHA^..newest-commit-SHA"
+ ```
- For example, suppose the commit A is the oldest, and its SHA is `4f5e4018c09ed797fdf446b3752f82e46f5af502`,
- and the commit D is the newest, and its SHA is `80e1c9e56783bd57bd7129828ec20b252ebc0538`.
- The cherry-pick command will be:
+ For example, suppose the commit A is the oldest, and its SHA is `4f5e4018c09ed797fdf446b3752f82e46f5af502`,
+ and the commit D is the newest, and its SHA is `80e1c9e56783bd57bd7129828ec20b252ebc0538`.
+ The cherry-pick command will be:
- ```shell
- git cherry-pick "4f5e4018c09ed797fdf446b3752f82e46f5af502^..80e1c9e56783bd57bd7129828ec20b252ebc0538"
- ```
+ ```shell
+ git cherry-pick "4f5e4018c09ed797fdf446b3752f82e46f5af502^..80e1c9e56783bd57bd7129828ec20b252ebc0538"
+ ```
- To cherry-pick a **merge commit**, use the flag `-m 1`. For example, suppose that the
merge commit SHA is `138f5e2f20289bb376caffa0303adb0cac859ce1`:
- ```shell
- git cherry-pick -m 1 138f5e2f20289bb376caffa0303adb0cac859ce1
- ```
-- To cherry-pick multiple commits, such as B and D in a range [A > B > C > D], use:
+ ```shell
+ git cherry-pick -m 1 138f5e2f20289bb376caffa0303adb0cac859ce1
+ ```
- ```shell
- git cherry-pick commit-B-SHA commit-D-SHA
- ```
+- To cherry-pick multiple commits, such as B and D in a range (A > B > C > D), use:
- For example, suppose commit B SHA = `4f5e4018c09ed797fdf446b3752f82e46f5af502`,
- and the commit D SHA = `80e1c9e56783bd57bd7129828ec20b252ebc0538`.
- The cherry-pick command will be:
+ ```shell
+ git cherry-pick commit-B-SHA commit-D-SHA
+ ```
- ```shell
- git cherry-pick 4f5e4018c09ed797fdf446b3752f82e46f5af502 80e1c9e56783bd57bd7129828ec20b252ebc0538
- ```
+ For example, suppose commit B SHA = `4f5e4018c09ed797fdf446b3752f82e46f5af502`,
+ and the commit D SHA = `80e1c9e56783bd57bd7129828ec20b252ebc0538`.
+ The cherry-pick command will be:
- This case is particularly useful when you have a merge commit in a sequence of
- commits and you want to cherry-pick all but the merge commit.
+ ```shell
+ git cherry-pick 4f5e4018c09ed797fdf446b3752f82e46f5af502 80e1c9e56783bd57bd7129828ec20b252ebc0538
+ ```
+
+ This case is particularly useful when you have a merge commit in a sequence of
+ commits and you want to cherry-pick all but the merge commit.
- If you push more commits to the CE branch, you can safely repeat the procedure
to cherry-pick them to the EE-equivalent branch. You can do that as many times as
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index 45b3d5a23a1..3ed586f07e9 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -35,7 +35,7 @@ the `author` field. GitLab team members **should not**.
- Any user-facing change **should** have a changelog entry. Example: "GitLab now
uses system fonts for all text."
-- Any change behind a feature flag **should not** have a changelog entry. The entry should be added [in the merge request removing the feature flags](https://docs.gitlab.com/ee/development/feature_flags.html#developing-with-feature-flags).
+- Any change behind a feature flag **should not** have a changelog entry. The entry should be added [in the merge request removing the feature flags](feature_flags/development.md).
- A fix for a regression introduced and then fixed in the same release (i.e.,
fixing a bug introduced during a monthly release candidate) **should not**
have a changelog entry.
@@ -62,7 +62,7 @@ making it both concise and descriptive, err on the side of descriptive.
The first example provides no context of where the change was made, or why, or
how it benefits the user.
-- **Bad:** Copy [some text] to clipboard.
+- **Bad:** Copy (some text) to clipboard.
- **Good:** Update the "Copy to clipboard" tooltip to indicate what's being
copied.
@@ -144,7 +144,7 @@ If you're working on the GitLab EE repository, the entry will be added to
| [`--type`](#--type-or--t) | `-t` | The category of the change, valid options are: `added`, `fixed`, `changed`, `deprecated`, `removed`, `security`, `performance`, `other` |
| `--help` | `-h` | Print help message |
-##### `--amend`
+#### `--amend`
You can pass the **`--amend`** argument to automatically stage the generated
file and amend it to the previous commit.
@@ -166,7 +166,7 @@ author:
type:
```
-##### `--force` or `-f`
+#### `--force` or `-f`
Use **`--force`** or **`-f`** to overwrite an existing changelog entry if it
already exists.
@@ -184,7 +184,7 @@ author:
type:
```
-##### `--merge-request` or `-m`
+#### `--merge-request` or `-m`
Use the **`--merge-request`** or **`-m`** argument to provide the
`merge_request` value:
@@ -199,7 +199,7 @@ author:
type:
```
-##### `--dry-run` or `-n`
+#### `--dry-run` or `-n`
Use the **`--dry-run`** or **`-n`** argument to prevent actually writing or
committing anything:
@@ -216,7 +216,7 @@ type:
$ ls changelogs/unreleased/
```
-##### `--git-username` or `-u`
+#### `--git-username` or `-u`
Use the **`--git-username`** or **`-u`** argument to automatically fill in the
`author` value with your configured Git `user.name` value:
@@ -234,7 +234,7 @@ author: Jane Doe
type:
```
-##### `--type` or `-t`
+#### `--type` or `-t`
Use the **`--type`** or **`-t`** argument to provide the `type` value:
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
index a7b402c3fb0..3b681880401 100644
--- a/doc/development/chatops_on_gitlabcom.md
+++ b/doc/development/chatops_on_gitlabcom.md
@@ -13,10 +13,10 @@ tasks such as:
To request access to Chatops on GitLab.com:
1. Log into <https://ops.gitlab.net/users/sign_in> using the same username as for GitLab.com.
-1. Ask [anyone in the `chatops` project](https://gitlab.com/gitlab-com/chatops/project_members) to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`.
+1. Ask [anyone in the `chatops` project](https://gitlab.com/gitlab-com/chatops/-/project_members) to add you by running `/chatops run member add <username> gitlab-com/chatops --ops`.
## See also
- - [Chatops Usage](https://docs.gitlab.com/ee/ci/chatops/README.html)
- - [Understanding EXPLAIN plans](understanding_explain_plans.md)
- - [Feature Groups](feature_flags/development.md#feature-groups)
+- [Chatops Usage](../ci/chatops/README.md)
+- [Understanding EXPLAIN plans](understanding_explain_plans.md)
+- [Feature Groups](feature_flags/development.md#feature-groups)
diff --git a/doc/development/code_comments.md b/doc/development/code_comments.md
index 36962eb46d4..827a610efa2 100644
--- a/doc/development/code_comments.md
+++ b/doc/development/code_comments.md
@@ -1,11 +1,11 @@
# Code comments
-Whenever you add comment to the code that is expected to be addressed at any time
-in future, please create a technical debt issue for it. Then put a link to it
+Whenever you add comment to the code that is expected to be addressed at any time
+in future, please create a technical debt issue for it. Then put a link to it
to the code comment you've created. This will allow other developers to quickly
check if a comment is still relevant and what needs to be done to address it.
-Examples:
+Examples:
```rb
# Deprecated scope until code_owner column has been migrated to rule_type.
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 6123f9f845a..e60800f1ab7 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -319,7 +319,7 @@ reviewee.
### GitLab-specific concerns
GitLab is used in a lot of places. Many users use
-our [Omnibus packages](https://about.gitlab.com/installation/), but some use
+our [Omnibus packages](https://about.gitlab.com/install/), but some use
the [Docker images](https://docs.gitlab.com/omnibus/docker/), some are
[installed from source](../install/installation.md),
and there are other installation methods available. GitLab.com itself is a large
diff --git a/doc/development/contributing/community_roles.md b/doc/development/contributing/community_roles.md
index b9c369286d2..3296cb173d7 100644
--- a/doc/development/contributing/community_roles.md
+++ b/doc/development/contributing/community_roles.md
@@ -4,8 +4,8 @@ GitLab community members and their privileges/responsibilities.
| Roles | Responsibilities | Requirements |
|-------|------------------|--------------|
-| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/team/). An expert on code reviews and knows the product/code base |
-| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/team/) |
+| Maintainer | Accepts merge requests on several GitLab projects | Added to the [team page](https://about.gitlab.com/company/team/). An expert on code reviews and knows the product/code base |
+| Reviewer | Performs code reviews on MRs | Added to the [team page](https://about.gitlab.com/company/team/) |
| Developer |Has access to GitLab internal infrastructure & issues (e.g. HR-related) | GitLab employee or a Core Team member (with an NDA) |
| Contributor | Can make contributions to all GitLab public projects | Have a GitLab.com account |
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 59cf5014da4..853882e8642 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is easy for everyone.
For a first-time step-by-step guide to the contribution process, please see
-["Contributing to GitLab"](https://about.gitlab.com/contributing/).
+["Contributing to GitLab"](https://about.gitlab.com/community/contribute/).
Looking for something to work on? Look for issues with the label [`Accepting merge requests`](#i-want-to-contribute).
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index f1015f56106..910f9f4bf7a 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -1,7 +1,7 @@
# Workflow labels
To allow for asynchronous issue handling, we use [milestones][milestones-page]
-and [labels][labels-page]. Leads and product managers handle most of the
+and [labels](https://gitlab.com/gitlab-org/gitlab-ce/-/labels). Leads and product managers handle most of the
scheduling into milestones. Labelling is a task for everyone.
Most issues will have labels for at least one of the following:
@@ -18,7 +18,7 @@ Most issues will have labels for at least one of the following:
- Severity: ~S1, ~S2, ~S3, ~S4
All labels, their meaning and priority are defined on the
-[labels page][labels-page].
+[labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels).
If you come across an issue that has none of these, and you're allowed to set
labels, you can _always_ add the team and type, and often also the subject.
@@ -38,7 +38,7 @@ makes them float to the top, depending on their importance.
Type labels are always lowercase, and can have any color, besides blue (which is
already reserved for subject labels).
-The descriptions on the [labels page][labels-page] explain what falls under each type label.
+The descriptions on the [labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels) explain what falls under each type label.
## Subject labels
@@ -89,7 +89,7 @@ The following team labels are **true** teams per our [organization structure](ht
- ~Delivery
- ~Documentation
-The descriptions on the [labels page][labels-page] explain what falls under the
+The descriptions on the [labels page](https://gitlab.com/gitlab-org/gitlab-ce/-/labels) explain what falls under the
responsibility of each team.
Within those team labels, we also have the ~backend and ~frontend labels to
@@ -154,8 +154,8 @@ The current group labels are:
* ~"group::ci and runner"
* ~"group::testing"
* ~"group::package"
-* ~"group::core release"
-* ~"group::supporting capabilities"
+* ~"group::progressive delivery"
+* ~"group::release management"
* ~"group::autodevops and kubernetes"
* ~"group::serverless and paas"
* ~"group::apm"
@@ -345,7 +345,7 @@ For feature proposals for EE, open an issue on the
In order to help track the feature proposals, we have created a
[`feature`][fl] label. For the time being, users that are not members
-of the project cannot add labels. You can instead ask one of the [core team]
+of the project cannot add labels. You can instead ask one of the [core team](https://about.gitlab.com/community/core-team/)
members to add the label ~feature to the issue or add the following
code snippet right after your description in a new line: `~feature`.
@@ -356,7 +356,7 @@ Please submit Feature Proposals using the ['Feature Proposal' issue template](ht
For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
-need to ask one of the [core team] members to add the label, if you do not have permissions to do it by yourself.
+need to ask one of the [core team](https://about.gitlab.com/community/core-team/) members to add the label, if you do not have permissions to do it by yourself.
If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab.
@@ -500,7 +500,6 @@ A recent example of this was the issue for
[Return to Contributing documentation](index.md)
-[labels-page]: https://gitlab.com/gitlab-org/gitlab-ce/labels
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
[inferred-labels]: https://gitlab.com/gitlab-org/quality/triage-ops/merge_requests/155
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 6064f59ed10..3f61ad7cb13 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -65,7 +65,7 @@ request is as follows:
1. If you are contributing documentation, choose `Documentation` from the
"Choose a template" menu and fill in the description according to the template.
1. Mention the issue(s) your merge request solves, using the `Solves #XXX` or
- `Closes #XXX` syntax to [auto-close](../../user/project/issues/automatic_issue_closing.md)
+ `Closes #XXX` syntax to [auto-close](../../user/project/issues/managing_issues.md#closing-issues-automatically)
the issue(s) once the merge request is merged.
1. If you're allowed to (Core team members, for example), set a relevant milestone
and [labels](issue_workflow.md).
@@ -193,6 +193,7 @@ requirements.
1. [Changelog entry added](../changelog.md), if necessary.
1. Reviewed by relevant (UX/FE/BE/tech writing) reviewers and all concerns are addressed.
1. Merged by a project maintainer.
+1. Confirmed to be working in the [Canary stage](https://about.gitlab.com/handbook/engineering/#canary-testing) or on GitLab.com once the contribution is deployed.
1. Added to the [release post](https://about.gitlab.com/handbook/marketing/blog/release-posts/),
if relevant.
1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml), if relevant.
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
index 87e61a7476f..5c6ea1f469d 100644
--- a/doc/development/contributing/style_guides.md
+++ b/doc/development/contributing/style_guides.md
@@ -1,11 +1,11 @@
# Style guides
-1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
+1. [Ruby](https://github.com/rubocop-hq/ruby-style-guide).
Important sections include [Source Code Layout][rss-source] and
[Naming][rss-naming]. Use:
- multi-line method chaining style **Option A**: dot `.` on the second line
- string literal quoting style **Option A**: single quoted by default
-1. [Rails](https://github.com/bbatsov/rails-style-guide)
+1. [Rails](https://github.com/rubocop-hq/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
1. [Testing][testing]
1. [JavaScript styleguide][js-styleguide]
@@ -13,7 +13,7 @@
1. [Shell commands (Ruby)](../shell_commands.md) created by GitLab
contributors to enhance security
1. [Database Migrations](../migration_style_guide.md)
-1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
+1. [Markdown](https://cirosantilli.com/markdown-style-guide/)
1. [Documentation styleguide](../documentation/styleguide.md)
1. Interface text should be written subjectively instead of objectively. It
should be the GitLab core team addressing a person. It should be written in
@@ -25,7 +25,7 @@
1. [Python](../python_guide/index.md)
This is also the style used by linting tools such as
-[RuboCop](https://github.com/bbatsov/rubocop) and [Hound CI](https://houndci.com).
+[RuboCop](https://github.com/rubocop-hq/rubocop) and [Hound CI](https://houndci.com).
---
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index de2c5b43411..0311eda1ff1 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -3,7 +3,7 @@
This section is to help give some copy-pasta you can use as a reference when you
run into some head-banging database problems.
-An easy first step is to search for your error in Slack or google "GitLab <my error>".
+An easy first step is to search for your error in Slack or google "GitLab (my error)".
---
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 6bfedcb1047..d9cea0614c3 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -393,7 +393,7 @@ Instead:
Example:
```md
-For more information, see the [confidential issue](https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) `https://gitlab.com/gitlab-org/gitlab-ce/issues/<issue_number>`.
+For more information, see the [confidential issue](../../user/project/issues/confidential_issues.md) `https://gitlab.com/gitlab-org/gitlab-ce/issues/<issue_number>`.
```
### Unlinking emails
@@ -518,7 +518,7 @@ you have your MR reviewed and approved by a technical writer.
```html
leave a blank line here
<div class="video-fallback">
- See the video: [Video title](https://www.youtube.com/watch?v=MqL6BMOySIQ).
+ See the video: <a href="https://www.youtube.com/watch?v=MqL6BMOySIQ">Video title</a>.
</div>
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe>
@@ -529,7 +529,7 @@ leave a blank line here
This is how it renders on docs.gitlab.com:
<div class="video-fallback">
- See the video: [What is GitLab](https://www.youtube.com/watch?v=enMumwvLAug).
+ See the video: <a href="https://www.youtube.com/watch?v=enMumwvLAug">What is GitLab</a>.
</div>
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/MqL6BMOySIQ" frameborder="0" allowfullscreen="true"> </iframe>
@@ -767,24 +767,24 @@ Other text includes deprecation notices and version-specific how-to information.
When a feature is available in EE-only tiers, add the corresponding tier according to the
feature availability:
-- For GitLab Starter and GitLab.com Bronze: `**[STARTER]**`.
-- For GitLab Premium and GitLab.com Silver: `**[PREMIUM]**`.
-- For GitLab Ultimate and GitLab.com Gold: `**[ULTIMATE]**`.
-- For GitLab Core and GitLab.com Free: `**[CORE]**`.
+- For GitLab Starter and GitLab.com Bronze: `**(STARTER)**`.
+- For GitLab Premium and GitLab.com Silver: `**(PREMIUM)**`.
+- For GitLab Ultimate and GitLab.com Gold: `**(ULTIMATE)**`.
+- For GitLab Core and GitLab.com Free: `**(CORE)**`.
To exclude GitLab.com tiers (when the feature is not available in GitLab.com), add the
keyword "only":
-- For GitLab Core: `**[CORE ONLY]**`.
-- For GitLab Starter: `**[STARTER ONLY]**`.
-- For GitLab Premium: `**[PREMIUM ONLY]**`.
-- For GitLab Ultimate: `**[ULTIMATE ONLY]**`.
+- For GitLab Core: `**(CORE ONLY)**`.
+- For GitLab Starter: `**(STARTER ONLY)**`.
+- For GitLab Premium: `**(PREMIUM ONLY)**`.
+- For GitLab Ultimate: `**(ULTIMATE ONLY)**`.
For GitLab.com only tiers (when the feature is not available for self-hosted instances):
-- For GitLab Bronze and higher tiers: `**[BRONZE ONLY]**`.
-- For GitLab Silver and higher tiers: `**[SILVER ONLY]**`.
-- For GitLab Gold: `**[GOLD ONLY]**`.
+- For GitLab Bronze and higher tiers: `**(BRONZE ONLY)**`.
+- For GitLab Silver and higher tiers: `**(SILVER ONLY)**`.
+- For GitLab Gold: `**(GOLD ONLY)**`.
The tier should be ideally added to headers, so that the full badge will be displayed.
However, it can be also mentioned from paragraphs, list items, and table cells. For these cases,
@@ -792,9 +792,9 @@ the tier mention will be represented by an orange question mark that will show t
For example:
-- `**[STARTER]**` renders as **[STARTER]**
-- `**[STARTER ONLY]**` renders as **[STARTER ONLY]**
-- `**[SILVER ONLY]**` renders as **[SILVER ONLY]**
+- `**(STARTER)**` renders as **(STARTER)**
+- `**(STARTER ONLY)**` renders as **(STARTER ONLY)**
+- `**(SILVER ONLY)**` renders as **(SILVER ONLY)**
The absence of tiers' mentions mean that the feature is available in GitLab Core,
GitLab.com Free, and all higher tiers.
@@ -802,7 +802,7 @@ GitLab.com Free, and all higher tiers.
### How it works
Introduced by [!244](https://gitlab.com/gitlab-com/gitlab-docs/merge_requests/244),
-the special markup `**[STARTER]**` will generate a `span` element to trigger the
+the special markup `**(STARTER)**` will generate a `span` element to trigger the
badges and tooltips (`<span class="badge-trigger starter">`). When the keyword
"only" is added, the corresponding GitLab.com badge will not be displayed.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 6f4a36d4066..7131b717353 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -182,52 +182,52 @@ There are a few gotchas with it:
pattern](https://en.wikipedia.org/wiki/Template_method_pattern).
For example, given this base:
- ```ruby
- class Base
- def execute
- return unless enabled?
+ ```ruby
+ class Base
+ def execute
+ return unless enabled?
- # ...
- # ...
- end
+ # ...
+ # ...
end
- ```
+ end
+ ```
- Instead of just overriding `Base#execute`, we should update it and extract
- the behaviour into another method:
+ Instead of just overriding `Base#execute`, we should update it and extract
+ the behaviour into another method:
- ```ruby
- class Base
- def execute
- return unless enabled?
+ ```ruby
+ class Base
+ def execute
+ return unless enabled?
- do_something
- end
+ do_something
+ end
- private
+ private
- def do_something
- # ...
- # ...
- end
+ def do_something
+ # ...
+ # ...
end
- ```
+ end
+ ```
- Then we're free to override that `do_something` without worrying about the
- guards:
+ Then we're free to override that `do_something` without worrying about the
+ guards:
- ```ruby
- module EE::Base
- extend ::Gitlab::Utils::Override
+ ```ruby
+ module EE::Base
+ extend ::Gitlab::Utils::Override
- override :do_something
- def do_something
- # Follow the above pattern to call super and extend it
- end
+ override :do_something
+ def do_something
+ # Follow the above pattern to call super and extend it
end
- ```
+ end
+ ```
- This would require updating CE first, or make sure this is back ported to CE.
+ This would require updating CE first, or make sure this is back ported to CE.
When prepending, place them in the `ee/` specific sub-directory, and
wrap class or module in `module EE` to avoid naming conflicts.
@@ -446,7 +446,6 @@ The disadvantage of this:
port `render_if_exists` to CE.
- If we have typos in the partial name, it would be silently ignored.
-
##### Caveats
The `render_if_exists` view path argument must be relative to `app/views/` and `ee/app/views`.
@@ -973,7 +972,7 @@ For regular JS files, the approach is similar.
1. An EE file should be created with the EE only code, and it should extend the CE counterpart.
1. For code inside functions that can't be extended, the code should be moved into a new file and we should use `ee_else_ce` helper:
-##### Example:
+#### Example:
```javascript
import eeCode from 'ee_else_ce/ee_code';
@@ -1000,7 +999,7 @@ styles are usually kept in stylesheet that is common for both CE and EE, and it
to isolate such ruleset from rest of CE rules (along with adding comment describing the same)
to avoid conflicts during CE to EE merge.
-#### Bad
+### Bad
```scss
.section-body {
@@ -1016,7 +1015,7 @@ to avoid conflicts during CE to EE merge.
}
```
-#### Good
+### Good
```scss
.section-body {
@@ -1034,13 +1033,13 @@ to avoid conflicts during CE to EE merge.
// EE-specific end
```
-### Backporting changes from EE to CE
+## Backporting changes from EE to CE
Until the work completed to merge the ce and ee codebases, which is tracked on [epic &802](https://gitlab.com/groups/gitlab-org/-/epics/802), there exists times in which some changes for EE require specific changes to the CE
code base. Examples of backports include the following:
-* Features intended or originally built for EE that are later decided to move to CE
-* Sometimes some code in CE may impact the EE feature
+- Features intended or originally built for EE that are later decided to move to CE
+- Sometimes some code in CE may impact the EE feature
Here is a workflow to make sure those changes end up backported safely into CE too.
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index 603a756ff56..0965db29557 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -1,4 +1,4 @@
-# Elasticsearch knowledge **[STARTER ONLY]**
+# Elasticsearch knowledge **(STARTER ONLY)**
This area is to maintain a compendium of useful information when working with elasticsearch.
@@ -150,7 +150,7 @@ Uses an [Edge NGram token filter](https://www.elastic.co/guide/en/elasticsearch/
## Troubleshooting
-### Getting "flood stage disk watermark [95%] exceeded"
+### Getting `flood stage disk watermark [95%] exceeded`
You might get an error such as
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 8baf343b133..e6af075a282 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -26,57 +26,57 @@ See the [Rails guides] for more info.
feature and fill in the details for your specific IMAP server and email
account:
- Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- address: "gitlab-incoming+%{key}@gmail.com"
-
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- user: "gitlab-incoming@gmail.com"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "imap.gmail.com"
- # IMAP server port
- port: 993
- # Whether the IMAP server uses SSL
- ssl: true
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
-
- As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
+ Configuration for Gmail / Google Apps, assumes mailbox `gitlab-incoming@gmail.com`:
+
+ ```yaml
+ incoming_email:
+ enabled: true
+
+ # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+ address: "gitlab-incoming+%{key}@gmail.com"
+
+ # Email account username
+ # With third party providers, this is usually the full email address.
+ # With self-hosted email servers, this is usually the user part of the email address.
+ user: "gitlab-incoming@gmail.com"
+ # Email account password
+ password: "[REDACTED]"
+
+ # IMAP server host
+ host: "imap.gmail.com"
+ # IMAP server port
+ port: 993
+ # Whether the IMAP server uses SSL
+ ssl: true
+ # Whether the IMAP server uses StartTLS
+ start_tls: false
+
+ # The mailbox where incoming mail will end up. Usually "inbox".
+ mailbox: "inbox"
+ # The IDLE command timeout.
+ idle_timeout: 60
+ ```
+
+ As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
1. Run this command in the GitLab root directory to launch `mail_room`:
- ```sh
- bundle exec mail_room -q -c config/mail_room.yml
- ```
+ ```sh
+ bundle exec mail_room -q -c config/mail_room.yml
+ ```
1. Verify that everything is configured correctly:
- ```sh
- bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
- ```
+ ```sh
+ bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
+ ```
1. Reply by email should now be working.
## Email namespace
-As of GitLab 11.7, we support a new format for email handler addresses. This was done to
+As of GitLab 11.7, we support a new format for email handler addresses. This was done to
support catch-all mailboxes.
If you need to implement a feature which requires a new email handler, follow these rules
@@ -91,10 +91,10 @@ for the format of the email key:
Examples of valid email keys:
- - `gitlab-org-gitlab-ce-20-Author_Token12345678-issue` (create a new issue)
- - `gitlab-org-gitlab-ce-20-Author_Token12345678-merge-request` (create a new merge request)
- - `1234567890abcdef1234567890abcdef-unsubscribe` (unsubscribe from a conversation)
- - `1234567890abcdef1234567890abcdef` (reply to a conversation)
+- `gitlab-org-gitlab-ce-20-Author_Token12345678-issue` (create a new issue)
+- `gitlab-org-gitlab-ce-20-Author_Token12345678-merge-request` (create a new merge request)
+- `1234567890abcdef1234567890abcdef-unsubscribe` (unsubscribe from a conversation)
+- `1234567890abcdef1234567890abcdef` (reply to a conversation)
Please note that the action `-issue-` is used in GitLab Premium as the handler for the Service Desk feature.
@@ -103,10 +103,10 @@ Please note that the action `-issue-` is used in GitLab Premium as the handler f
Although we continue to support the older legacy format, no new features should use a legacy format.
These are the only valid legacy formats for an email handler:
- - `path/to/project+namespace`
- - `path/to/project+namespace+action`
- - `namespace`
- - `namespace+action`
+- `path/to/project+namespace`
+- `path/to/project+namespace+action`
+- `namespace`
+- `namespace+action`
Please note that `path/to/project` is used in GitLab Premium as handler for the Service Desk feature.
diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md
index c67389b169e..49b74b5ebcf 100644
--- a/doc/development/fe_guide/architecture.md
+++ b/doc/development/fe_guide/architecture.md
@@ -11,7 +11,7 @@ Architectural decisions should be accessible to everyone, so please document
them in the relevant Merge Request discussion or by updating our documentation
when appropriate.
-You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/team).
+You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/company/team).
## Examples
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index f3fdaa3b883..ae0e2361840 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -12,8 +12,7 @@ This checklist is intended to help us during development of bigger features/refa
Please use your best judgement when to use it and please contribute new points through merge requests if something comes to your mind.
----
-
+```
### Frontend development
#### Planning development
@@ -24,15 +23,15 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] Are all necessary UX specifications available that you will need in order to implement? Are there new UX components/patterns in the designs? Then contact the UI component team early on. How should error messages or validation be handled?
- [ ] **Library usage** Use Vuex as soon as you have even a medium state to manage, use Vue router if you need to have different views internally and want to link from the outside. Check what libraries we already have for which occasions.
- [ ] **Plan your implementation:**
- - [ ] **Architecture plan:** Create a plan aligned with GitLab's architecture, how you are going to do the implementation, for example Vue application setup and its components (through [onion skinning](https://gitlab.com/gitlab-org/gitlab-ce/issues/35873#note_39994091)), Store structure and data flow, which existing Vue components can you reuse. It's a good idea to go through your plan with another engineer to refine it.
- - [ ] **Backend:** The best way is to kickoff the implementation in a call and discuss with the assigned Backend engineer what you will need from the backend and also when. Can you reuse existing API's? How is the performance with the planned architecture? Maybe create together a JSON mock object to already start with development.
- - [ ] **Communication:** It also makes sense to have for bigger features an own slack channel (normally called #f_{feature_name}) and even weekly demo calls with all people involved.
- - [ ] **Dependency Plan:** Are there big dependencies in the plan between you and others, then maybe create an execution diagram to show what is blocking which part and the order of the different parts.
- - [ ] **Task list:** Create a simple checklist of the subtasks that are needed for the implementation, also consider creating even sub issues. (for example show a comment, delete a comment, update a comment, etc.). This helps you and also everyone else following the implementation
+ - [ ] **Architecture plan:** Create a plan aligned with GitLab's architecture, how you are going to do the implementation, for example Vue application setup and its components (through [onion skinning](https://gitlab.com/gitlab-org/gitlab-ce/issues/35873#note_39994091)), Store structure and data flow, which existing Vue components can you reuse. It's a good idea to go through your plan with another engineer to refine it.
+ - [ ] **Backend:** The best way is to kickoff the implementation in a call and discuss with the assigned Backend engineer what you will need from the backend and also when. Can you reuse existing API's? How is the performance with the planned architecture? Maybe create together a JSON mock object to already start with development.
+ - [ ] **Communication:** It also makes sense to have for bigger features an own slack channel (normally called #f_{feature_name}) and even weekly demo calls with all people involved.
+ - [ ] **Dependency Plan:** Are there big dependencies in the plan between you and others, then maybe create an execution diagram to show what is blocking which part and the order of the different parts.
+ - [ ] **Task list:** Create a simple checklist of the subtasks that are needed for the implementation, also consider creating even sub issues. (for example show a comment, delete a comment, update a comment, etc.). This helps you and also everyone else following the implementation
- [ ] **Keep it small** To make it easier for you and also all reviewers try to keep merge requests small and merge into a feature branch if needed. To accomplish that you need to plan that from the start. Different methods are:
- - [ ] **Skeleton based plan** Start with an MR that has the skeleton of the components with placeholder content. In following MRs you can fill the components with interactivity. This also makes it easier to spread out development on multiple people.
- - [ ] **Cookie Mode** Think about hiding the feature behind a cookie flag if the implementation is on top of existing features
- - [ ] **New route** Are you refactoring something big then you might consider adding a new route where you implement the new feature and when finished delete the current route and rename the new one. (for example 'merge_request' and 'new_merge_request')
+ - [ ] **Skeleton based plan** Start with an MR that has the skeleton of the components with placeholder content. In following MRs you can fill the components with interactivity. This also makes it easier to spread out development on multiple people.
+ - [ ] **Cookie Mode** Think about hiding the feature behind a cookie flag if the implementation is on top of existing features
+ - [ ] **New route** Are you refactoring something big then you might consider adding a new route where you implement the new feature and when finished delete the current route and rename the new one. (for example 'merge_request' and 'new_merge_request')
- [ ] **Setup** Is there any specific setup needed for your implementation (for example a kubernetes cluster)? Then let everyone know if it is not already mentioned where they can find documentation (if it doesn't exist - create it)
- [ ] **Security** Are there any new security relevant implementations? Then please contact the security team for an app security review. If you are not sure ask our [domain expert](https://about.gitlab.com/handbook/engineering/frontend/#frontend-domain-experts)
@@ -57,8 +56,7 @@ Please use your best judgement when to use it and please contribute new points t
- [ ] Are there any big changes on how and especially how frequently we use the API then let production know about it
- [ ] Smoke test of the RC on dev., staging., canary deployments and .com
- [ ] Follow up on issues that came out of the review. Create issues for discovered edge cases that should be covered in future iterations.
-
----
+```
### Share your work early
@@ -66,7 +64,7 @@ Please use your best judgement when to use it and please contribute new points t
GitLab's architecture.
1. Add a diagram to the issue and ask a frontend architect in the slack channel `#fe_architectural` about it.
- ![Diagram of Issue Boards Architecture](img/boards_diagram.png)
+ ![Diagram of Issue Boards Architecture](img/boards_diagram.png)
1. Don't take more than one week between starting work on a feature and
sharing a Merge Request with a reviewer or a maintainer.
diff --git a/doc/development/fe_guide/emojis.md b/doc/development/fe_guide/emojis.md
index 38794c47965..6d324d4c4a0 100644
--- a/doc/development/fe_guide/emojis.md
+++ b/doc/development/fe_guide/emojis.md
@@ -3,10 +3,10 @@
GitLab supports native unicode emojis and fallsback to image-based emojis selectively
when your platform does not support it.
-# How to update Emojis
+## How to update Emojis
1. Update the `gemojione` gem
- 1. Update `fixtures/emojis/index.json` from [Gemojione](https://github.com/jonathanwiesel/gemojione/blob/master/config/index.json).
+ 1. Update `fixtures/emojis/index.json` from [Gemojione](https://github.com/bonusly/gemojione/blob/master/config/index.json).
In the future, we could grab the file directly from the gem.
We should probably make a PR on the Gemojione project to get access to
all emojis after being parsed or just a raw path to the `json` file itself.
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 9fcd32fddfa..55b719227e5 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -55,7 +55,6 @@ It is possible to manage an application state with Apollo by passing
in a resolvers object when creating the default client. The default state can be set by writing
to the cache after setting up the default client.
-
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@@ -115,13 +114,12 @@ defaultClient.query(query)
.then(result => console.log(result));
```
-Read more about the [Apollo] client in the [Apollo documentation][apollo-client-docs].
+Read more about the [Apollo] client in the [Apollo documentation](https://www.apollographql.com/docs/tutorial/client/).
[Apollo]: https://www.apollographql.com/
[vue-apollo]: https://github.com/Akryum/vue-apollo/
[vue-apollo-docs]: https://akryum.github.io/vue-apollo/
[feature-flags]: ../feature_flags.md
[default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js
-[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html
[vue-test-utils]: https://vue-test-utils.vuejs.org/
[apollo-link-state]: https://www.apollographql.com/docs/link/links/state.html
diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md
index 83bb449e54d..47ac87fc895 100644
--- a/doc/development/fe_guide/security.md
+++ b/doc/development/fe_guide/security.md
@@ -1,5 +1,6 @@
# Security
-### Resources
+
+## Resources
[Mozilla’s HTTP Observatory CLI][observatory-cli] and the
[Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding
@@ -56,7 +57,7 @@ Some resources on implementing Subresource Integrity:
-->
-### Including external resources
+## Including external resources
External fonts, CSS, and JavaScript should never be used with the exception of
Google Analytics and Piwik - and only when the instance has enabled it. Assets
@@ -64,7 +65,7 @@ should always be hosted and served locally from the GitLab instance. Embedded
resources via `iframes` should never be used except in certain circumstances
such as with ReCaptcha, which cannot be used without an `iframe`.
-### Avoiding inline scripts and styles
+## Avoiding inline scripts and styles
In order to protect users from [XSS vulnerabilities][xss], we will disable
inline scripts in the future using Content Security Policy.
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index c67467b7c11..739f4207e27 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -5,7 +5,7 @@ GitLab Inc. provided environments such as staging and production, you need to
have access to the chatops bot. Chatops bot is currently running on the ops instance,
which is different from GitLab.com or dev.gitlab.org.
-Follow the Chatops document to [request access](https://docs.gitlab.com/ee/development/chatops_on_gitlabcom.html#requesting-access).
+Follow the Chatops document to [request access](../chatops_on_gitlabcom.md#requesting-access).
Once you are added to the project test if your access propagated,
run:
@@ -112,7 +112,7 @@ instances. Make sure to add the ~"feature flag" label to this merge request so
release managers are aware the changes are hidden behind a feature flag. If the
merge request has to be picked into a stable branch, make sure to also add the
appropriate "Pick into X" label (e.g. "Pick into XX.X").
-See [the process document](https://docs.gitlab.com/ee/development/feature_flags/process.html#including-a-feature-behind-feature-flag-in-the-final-release) for further details.
+See [the process document](process.md#including-a-feature-behind-feature-flag-in-the-final-release) for further details.
When a feature gate has been removed from the code base, the value still exists
in the database.
diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md
index 238052529d9..98773026122 100644
--- a/doc/development/feature_flags/development.md
+++ b/doc/development/feature_flags/development.md
@@ -57,7 +57,7 @@ the feature flag check will default to `true`.
As an example, if you were to ship the backend half of a feature behind a flag,
you'd want to explicitly disable that flag until the frontend half is also ready
-to be shipped. [You can do this via Chatops](https://docs.gitlab.com/ee/development/feature_flags/controls.html):
+to be shipped. [You can do this via Chatops](controls.md):
```
/chatops run feature set some_feature 0
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index 18e4dc2ca0c..02874d18a30 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -33,9 +33,9 @@ they are still not 100% standardized. You can see them below:
| User avatars | yes | uploads/-/system/user/avatar/:id/:filename | `AvatarUploader` | User |
| User snippet attachments | yes | uploads/-/system/personal_snippet/:id/:random_hex/:filename | `PersonalFileUploader` | Snippet |
| Project avatars | yes | uploads/-/system/project/avatar/:id/:filename | `AvatarUploader` | Project |
-| Issues/MR/Notes Markdown attachments | yes | uploads/:project_path_with_namespace/:random_hex/:filename | `FileUploader` | Project |
-| Issues/MR/Notes Legacy Markdown attachments | no | uploads/-/system/note/attachment/:id/:filename | `AttachmentUploader` | Note |
-| CI Artifacts (CE) | yes | shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id (:disk_hash is SHA256 digest of project_id) | `JobArtifactUploader` | Ci::JobArtifact |
+| Issues/MR/Notes Markdown attachments | yes | uploads/:project_path_with_namespace/:random_hex/:filename | `FileUploader` | Project |
+| Issues/MR/Notes Legacy Markdown attachments | no | uploads/-/system/note/attachment/:id/:filename | `AttachmentUploader` | Note |
+| CI Artifacts (CE) | yes | `shared/artifacts/:disk_hash[0..1]/:disk_hash[2..3]/:disk_hash/:year_:month_:date/:job_id/:job_artifact_id` (:disk_hash is SHA256 digest of project_id) | `JobArtifactUploader` | Ci::JobArtifact |
| LFS Objects (CE) | yes | shared/lfs-objects/:hex/:hex/:object_hash | `LfsObjectUploader` | LfsObject |
| External merge request diffs | yes | shared/external-diffs/merge_request_diffs/mr-:parent_id/diff-:id | `ExternalDiffUploader` | MergeRequestDiff |
diff --git a/doc/development/geo.md b/doc/development/geo.md
index a10f13b069f..685d4e44ad3 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -1,4 +1,4 @@
-# Geo (development) **[PREMIUM ONLY]**
+# Geo (development) **(PREMIUM ONLY)**
Geo connects GitLab instances together. One GitLab instance is
designated as a **primary** node and can be run with multiple
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index f961a2fc837..f09339eb3a4 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -41,7 +41,7 @@ of possible security breaches in our code:
Remember to run
[SAST](../../user/application_security/sast/index.md)
-**[ULTIMATE]** on your project (or at least the [gosec
+**(ULTIMATE)** on your project (or at least the [gosec
analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/gosec)),
and to follow our [Security
requirements](../code_review.md#security-requirements).
@@ -95,9 +95,9 @@ Dependencies should be kept to the minimum. The introduction of a new
dependency should be argued in the merge request, as per our [Approval
Guidelines](../code_review.md#approval-guidelines). Both [License
Management](../../user/project/merge_requests/license_management.md)
-**[ULTIMATE]** and [Dependency
+**(ULTIMATE)** and [Dependency
Scanning](../../user/application_security/dependency_scanning/index.md)
-**[ULTIMATE]** should be activated on all projects to ensure new dependencies
+**(ULTIMATE)** should be activated on all projects to ensure new dependencies
security status and license compatibility.
### Modules
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 1b9ebb50c29..13dda17bb7d 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -101,10 +101,10 @@ end
in a prepended module, which is very likely the case in EE. We could see
error like this:
- ```
- 1.1) Failure/Error: expect_any_instance_of(ApplicationSetting).to receive_messages(messages)
- Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported.
- ```
+ ```
+ 1.1) Failure/Error: expect_any_instance_of(ApplicationSetting).to receive_messages(messages)
+ Using `any_instance` to stub a method (elasticsearch_indexing) that has been defined on a prepended module (EE::ApplicationSetting) is not supported.
+ ```
### Alternative: `expect_next_instance_of`
diff --git a/doc/development/integrations/jira_connect.md b/doc/development/integrations/jira_connect.md
index 5bf43d320c6..9ba3b922fd8 100644
--- a/doc/development/integrations/jira_connect.md
+++ b/doc/development/integrations/jira_connect.md
@@ -4,7 +4,7 @@ The following are required to install and test the app:
1. A Jira Cloud instance
- Atlassian provides free instances for development and testing. [Click here to sign up](http://go.atlassian.com/cloud-dev).
+ Atlassian provides free instances for development and testing. [Click here to sign up](https://developer.atlassian.com/platform/marketplace/getting-started/#free-developer-instances-to-build-and-test-your-app).
1. A GitLab instance available over the internet
@@ -15,7 +15,7 @@ The following are required to install and test the app:
> This feature is currently behind the `:jira_connect_app` feature flag
-# Installing the app in Jira
+## Installing the app in Jira
1. Enable Jira development mode to install apps that are not from the Atlassian Marketplace
diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md
index 6f3dd59b2c3..80ec7b8c0cf 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -1,18 +1,18 @@
-# Licensed feature availability **[STARTER]**
+# Licensed feature availability **(STARTER)**
-As of GitLab 9.4, we've been supporting a simplified version of licensed
-feature availability checks via `ee/app/models/license.rb`, both for
+As of GitLab 9.4, we've been supporting a simplified version of licensed
+feature availability checks via `ee/app/models/license.rb`, both for
on-premise or GitLab.com plans and features.
## Restricting features scoped by namespaces or projects
GitLab.com plans are persisted on user groups and namespaces, therefore, if you're adding a
-feature such as [Related issues](../user/project/issues/related_issues.md) or
-[Service desk](../user/project/service_desk.md),
+feature such as [Related issues](../user/project/issues/related_issues.md) or
+[Service desk](../user/project/service_desk.md),
it should be restricted on namespace scope.
-1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
- `ee/app/models/license.rb`. Note on `ee/app/models/ee/namespace.rb` that _Bronze_ GitLab.com
+1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
+ `ee/app/models/license.rb`. Note on `ee/app/models/ee/namespace.rb` that _Bronze_ GitLab.com
features maps to on-premise _EES_, _Silver_ to _EEP_ and _Gold_ to _EEU_.
2. Check using:
@@ -22,12 +22,12 @@ project.feature_available?(:feature_symbol)
## Restricting global features (instance)
-However, for features such as [Geo](../administration/geo/replication/index.md) and
-[Load balancing](../administration/database_load_balancing.md), which cannot be restricted
-to only a subset of projects or namespaces, the check will be made directly in
+However, for features such as [Geo](../administration/geo/replication/index.md) and
+[Load balancing](../administration/database_load_balancing.md), which cannot be restricted
+to only a subset of projects or namespaces, the check will be made directly in
the instance license.
-1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
+1. Add the feature symbol on `EES_FEATURES`, `EEP_FEATURES` or `EEU_FEATURES` constants in
`ee/app/models/license.rb`.
2. Add the same feature symbol to `GLOBAL_FEATURES`
3. Check using:
diff --git a/doc/development/logging.md b/doc/development/logging.md
index d61441813b2..4f63c84fc0e 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -30,8 +30,8 @@ Completed 200 OK in 166ms (Views: 117.4ms | ActiveRecord: 27.2ms)
These logs suffer from a number of problems:
1. They often lack timestamps or other contextual information (e.g. project ID, user)
-2. They may span multiple lines, which make them hard to find via Elasticsearch.
-3. They lack a common structure, which make them hard to parse by log
+1. They may span multiple lines, which make them hard to find via Elasticsearch.
+1. They lack a common structure, which make them hard to parse by log
forwarders, such as Logstash or Fluentd. This also makes them hard to
search.
@@ -67,46 +67,46 @@ importer progresses. Here's what to do:
make it easy for people to search pertinent logs in one place. For
example, `geo.log` contains all logs pertaining to GitLab Geo.
To create a new file:
- 1. Choose a filename (e.g. `importer_json.log`).
- 1. Create a new subclass of `Gitlab::JsonLogger`:
-
- ```ruby
- module Gitlab
- module Import
- class Logger < ::Gitlab::JsonLogger
- def self.file_name_noext
- 'importer'
- end
+ 1. Choose a filename (e.g. `importer_json.log`).
+ 1. Create a new subclass of `Gitlab::JsonLogger`:
+
+ ```ruby
+ module Gitlab
+ module Import
+ class Logger < ::Gitlab::JsonLogger
+ def self.file_name_noext
+ 'importer'
end
- end
- end
- ```
+ end
+ end
+ end
+ ```
- 1. In your class where you want to log, you might initialize the logger as an instance variable:
+ 1. In your class where you want to log, you might initialize the logger as an instance variable:
- ```ruby
- attr_accessor :logger
+ ```ruby
+ attr_accessor :logger
- def initialize
- @logger = Gitlab::Import::Logger.build
- end
- ```
+ def initialize
+ @logger = Gitlab::Import::Logger.build
+ end
+ ```
- Note that it's useful to memoize this because creating a new logger
- each time you log will open a file, adding unnecessary overhead.
+ Note that it's useful to memoize this because creating a new logger
+ each time you log will open a file, adding unnecessary overhead.
1. Now insert log messages into your code. When adding logs,
make sure to include all the context as key-value pairs:
- ```ruby
- # BAD
- logger.info("Unable to create project #{project.id}")
- ```
+ ```ruby
+ # BAD
+ logger.info("Unable to create project #{project.id}")
+ ```
- ```ruby
- # GOOD
- logger.info(message: "Unable to create project", project_id: project.id)
- ```
+ ```ruby
+ # GOOD
+ logger.info(message: "Unable to create project", project_id: project.id)
+ ```
1. Be sure to create a common base structure of your log messages. For example,
all messages might have `current_user_id` and `project_id` to make it easier
@@ -116,16 +116,16 @@ importer progresses. Here's what to do:
logs properly if you [mix integer and string
types](https://www.elastic.co/guide/en/elasticsearch/guide/current/mapping.html#_avoiding_type_gotchas):
- ```ruby
- # BAD
- logger.info(message: "Import error", error: 1)
- logger.info(message: "Import error", error: "I/O failure")
- ```
+ ```ruby
+ # BAD
+ logger.info(message: "Import error", error: 1)
+ logger.info(message: "Import error", error: "I/O failure")
+ ```
- ```ruby
- # GOOD
- logger.info(message: "Import error", error_code: 1, error: "I/O failure")
- ```
+ ```ruby
+ # GOOD
+ logger.info(message: "Import error", error_code: 1, error: "I/O failure")
+ ```
## Additional steps with new log files
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 9b26f691b55..0c7601b415e 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -21,7 +21,7 @@ When downtime is necessary the migration has to be approved by:
1. A Database Specialist
An up-to-date list of people holding these titles can be found at
-<https://about.gitlab.com/team/>.
+<https://about.gitlab.com/company/team/>.
When writing your migrations, also consider that databases might have stale data
or inconsistencies and guard for that. Try to make as few assumptions as
diff --git a/doc/development/new_fe_guide/development/components.md b/doc/development/new_fe_guide/development/components.md
index 963ce53423b..cebdc87eab9 100644
--- a/doc/development/new_fe_guide/development/components.md
+++ b/doc/development/new_fe_guide/development/components.md
@@ -13,7 +13,7 @@ D3 is very popular across many projects outside of GitLab:
- [The New York Times](https://archive.nytimes.com/www.nytimes.com/interactive/2012/02/13/us/politics/2013-budget-proposal-graphic.html)
- [plot.ly](https://plot.ly/)
-- [Droptask](https://www.droptask.com/)
+- [Droptask](https://www.ayoa.com/previously-droptask/)
Within GitLab, D3 has been used for the following notable features
diff --git a/doc/development/new_fe_guide/development/performance.md b/doc/development/new_fe_guide/development/performance.md
index 640a8d64176..c54b8305991 100644
--- a/doc/development/new_fe_guide/development/performance.md
+++ b/doc/development/new_fe_guide/development/performance.md
@@ -2,10 +2,10 @@
## Monitoring
-We have a performance dashboard available in one of our [grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://sitespeed.io) every 6 hours. These changes are displayed after a set number of pages are aggregated.
+We have a performance dashboard available in one of our [grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://www.sitespeed.io/) every 6 hours. These changes are displayed after a set number of pages are aggregated.
These pages can be found inside a text file in the gitlab-build-images [repository](https://gitlab.com/gitlab-org/gitlab-build-images) called [gitlab.txt](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/scripts/gitlab.txt)
-Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/team) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
+Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
There are 3 recommended high impact metrics to review on each page:
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index 8441089418e..2b62c2a41fe 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -261,7 +261,7 @@ scenario 'successfully', :js do
end
```
-The steps of each test are written using capybara methods ([documentation](http://www.rubydoc.info/gems/capybara/2.15.1)).
+The steps of each test are written using capybara methods ([documentation](https://www.rubydoc.info/gems/capybara/2.15.1)).
Bear in mind <abbr title="XMLHttpRequest">XHR</abbr> calls might require you to use `wait_for_requests` in between steps, like so:
@@ -277,7 +277,7 @@ expect(page).not_to have_selector('.card')
### Vuex Helper: `testAction`
-We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/en/testing.html):
+We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/guide/testing.html):
```
testAction(
diff --git a/doc/development/new_fe_guide/style/html.md b/doc/development/new_fe_guide/style/html.md
index e8c9c2ccebf..1445da3f0e1 100644
--- a/doc/development/new_fe_guide/style/html.md
+++ b/doc/development/new_fe_guide/style/html.md
@@ -16,7 +16,7 @@ Button tags requires a `type` attribute according to the [W3C HTML specification
### Button role
-If an HTML element has an `onClick` handler but is not a button, it should have `role="button"`. This is [more accessible](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role).
+If an HTML element has an `onClick` handler but is not a button, it should have `role="button"`. This is [more accessible](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role).
```html
// bad
diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md
index 5f7210020b6..a13adc2f13e 100644
--- a/doc/development/newlines_styleguide.md
+++ b/doc/development/newlines_styleguide.md
@@ -11,7 +11,7 @@ def method
issue.save
- render json: issue
+ render json: issue
end
```
@@ -21,7 +21,7 @@ def method
issue = Issue.new
issue.save
- render json: issue
+ render json: issue
end
```
diff --git a/doc/development/packages.md b/doc/development/packages.md
index ab0c5f9904d..08aa0b08525 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -1,15 +1,15 @@
-# Packages **[PREMIUM]**
+# Packages **(PREMIUM)**
This document will guide you through adding another [package management system](../administration/packages.md) support to GitLab.
See already supported package types in [Packages documentation](../administration/packages.md)
Since GitLab packages' UI is pretty generic, it is possible to add new
-package system support by solely backend changes. This guide is superficial and does
-not cover the way the code should be written. However, you can find a good example
-by looking at existing merge requests with Maven and NPM support:
+package system support by solely backend changes. This guide is superficial and does
+not cover the way the code should be written. However, you can find a good example
+by looking at existing merge requests with Maven and NPM support:
-- [NPM registry support](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8673).
+- [NPM registry support](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8673).
- [Maven repository](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6607).
- [Instance level endpoint for Maven repository](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8757)
@@ -17,44 +17,44 @@ by looking at existing merge requests with Maven and NPM support:
The existing database model requires the following:
-- Every package belongs to a project.
+- Every package belongs to a project.
- Every package file belongs to a package.
- A package can have one or more package files.
- The package model is based on storing information about the package and its version.
## API endpoints
-Package systems work with GitLab via API. For example `ee/lib/api/npm_packages.rb`
-implements API endpoints to work with NPM clients. So, the first thing to do is to
-add a new `ee/lib/api/your_name_packages.rb` file with API endpoints that are
-necessary to make the package system client to work. Usually that means having
-endpoints like:
+Package systems work with GitLab via API. For example `ee/lib/api/npm_packages.rb`
+implements API endpoints to work with NPM clients. So, the first thing to do is to
+add a new `ee/lib/api/your_name_packages.rb` file with API endpoints that are
+necessary to make the package system client to work. Usually that means having
+endpoints like:
- GET package information.
- GET package file content.
- PUT upload package.
Since the packages belong to a project, it's expected to have project-level endpoint
-for uploading and downloading them. For example:
+for uploading and downloading them. For example:
```
GET https://gitlab.com/api/v4/projects/<your_project_id>/packages/npm/
PUT https://gitlab.com/api/v4/projects/<your_project_id>/packages/npm/
```
-Group-level and instance-level endpoints are good to have but are optional.
+Group-level and instance-level endpoints are good to have but are optional.
NOTE: **Note:**
-To avoid name conflict for instance-level endpoints we use
+To avoid name conflict for instance-level endpoints we use
[the package naming convention](../user/project/packages/npm_registry.md#package-naming-convention)
## Configuration
-GitLab has a `packages` section in its configuration file (`gitlab.rb`).
-It applies to all package systems supported by GitLab. Usually you don't need
-to add anything there.
+GitLab has a `packages` section in its configuration file (`gitlab.rb`).
+It applies to all package systems supported by GitLab. Usually you don't need
+to add anything there.
-Packages can be configured to use object storage, therefore your code must support it.
+Packages can be configured to use object storage, therefore your code must support it.
## Database
@@ -63,6 +63,6 @@ Every time you upload a new package, you can either create a new record of `Pack
or add files to existing record. `PackageFile` should be able to store all file-related
information like the file `name`, `side`, `sha1`, etc.
-If there is specific data necessary to be stored for only one package system support,
-consider creating a separate metadata model. See `packages_maven_metadata` table
+If there is specific data necessary to be stored for only one package system support,
+consider creating a separate metadata model. See `packages_maven_metadata` table
and `Packages::MavenMetadatum` model as example for package specific data.
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 795523b82aa..e1d1d2e33fa 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -95,7 +95,9 @@ Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_
available when running GitLab in development mode _and_ when setting the
environment variable `ENABLE_SHERLOCK` to a non empty value. For example:
- ENABLE_SHERLOCK=1 bundle exec rails s
+```sh
+ENABLE_SHERLOCK=1 bundle exec rails s
+```
Recorded transactions can be found by navigating to `/sherlock/transactions`.
@@ -106,7 +108,9 @@ Bullet adds quite a bit of logging noise it's disabled by default. To enable
Bullet, set the environment variable `ENABLE_BULLET` to a non-empty value before
starting GitLab. For example:
- ENABLE_BULLET=true bundle exec rails s
+```sh
+ENABLE_BULLET=true bundle exec rails s
+```
Bullet will log query problems to both the Rails log as well as the Chrome
console.
diff --git a/doc/development/python_guide/index.md b/doc/development/python_guide/index.md
index 6025dc9ebf2..a80bee27d4a 100644
--- a/doc/development/python_guide/index.md
+++ b/doc/development/python_guide/index.md
@@ -36,7 +36,7 @@ You can read more about it in: <https://github.com/pyenv/pyenv-installer#prerequ
Pyenv installation will add required changes to Bash. If you use a different shell,
check for any additional steps required for it.
-For Fish, you can install a plugin for [Fisherman](https://github.com/fisherman/fisherman):
+For Fish, you can install a plugin for [Fisher](https://github.com/jorgebucaran/fisher):
```bash
fisher add fisherman/pyenv
@@ -76,4 +76,3 @@ pipenv shell
After running that command, you can run GitLab on the same shell and it will be using the Python and dependencies
installed from the `pipenv install` command.
-
diff --git a/doc/development/query_recorder.md b/doc/development/query_recorder.md
index 2167ed57428..a6b60149ea4 100644
--- a/doc/development/query_recorder.md
+++ b/doc/development/query_recorder.md
@@ -1,6 +1,6 @@
# QueryRecorder
-QueryRecorder is a tool for detecting the [N+1 queries problem](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) from tests.
+QueryRecorder is a tool for detecting the [N+1 queries problem](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) from tests.
> Implemented in [spec/support/query_recorder.rb](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/support/helpers/query_recorder.rb) via [9c623e3e](https://gitlab.com/gitlab-org/gitlab-ce/commit/9c623e3e5d7434f2e30f7c389d13e5af4ede770a)
@@ -86,4 +86,4 @@ QueryRecorder SQL: SELECT COUNT(*) FROM "issues" WHERE "issues"."deleted_at" IS
- [Bullet](profiling.md#Bullet) For finding `N+1` query problems
- [Performance guidelines](performance.md)
-- [Merge request performance guidelines](merge_request_performance_guidelines.md#query-counts) \ No newline at end of file
+- [Merge request performance guidelines](merge_request_performance_guidelines.md#query-counts)
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 4fc10b6af5c..c97e179910b 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -28,7 +28,7 @@ bin/rake "gitlab:seed:issues[group-path/project-path]"
By default, this seeds an average of 2 issues per week for the last 5 weeks per
project.
-#### Seeding issues for Insights charts **[ULTIMATE]**
+#### Seeding issues for Insights charts **(ULTIMATE)**
You can seed issues specifically for working with the
[Insights charts](../user/group/insights/index.md) with the
diff --git a/doc/development/routing.md b/doc/development/routing.md
index e9c0ad8d4e8..a25eb48b73c 100644
--- a/doc/development/routing.md
+++ b/doc/development/routing.md
@@ -7,11 +7,15 @@ support subgroups, GitLab project and group routes use the wildcard
character to match project and group routes. For example, we might have
a path such as:
- /gitlab-com/customer-success/north-america/west/customerA
+```
+/gitlab-com/customer-success/north-america/west/customerA
+```
However, paths can be ambiguous. Consider the following example:
- /gitlab-com/edit
+```
+/gitlab-com/edit
+```
It's ambiguous whether there is a subgroup named `edit` or whether
this is a special endpoint to edit the `gitlab-com` group.
@@ -25,8 +29,10 @@ number of [reserved names](../user/reserved_names.md).
We have a number of global routes. For example:
- /-/health
- /-/metrics
+```
+/-/health
+/-/metrics
+```
## Group routes
@@ -34,10 +40,12 @@ Every group route must be under the `/-/` scope.
Examples:
- gitlab-org/-/edit
- gitlab-org/-/activity
- gitlab-org/-/security/dashboard
- gitlab-org/serverless/-/activity
+```
+gitlab-org/-/edit
+gitlab-org/-/activity
+gitlab-org/-/security/dashboard
+gitlab-org/serverless/-/activity
+```
To achieve that, use the `scope '-'` method.
@@ -48,10 +56,12 @@ client or other software requires something different.
Examples:
- gitlab-org/gitlab-ce/-/activity
- gitlab-org/gitlab-ce/-/jobs/123
- gitlab-org/gitlab-ce/-/settings/repository
- gitlab-org/serverless/runtimes/-/settings/repository
+```
+gitlab-org/gitlab-ce/-/activity
+gitlab-org/gitlab-ce/-/jobs/123
+gitlab-org/gitlab-ce/-/settings/repository
+gitlab-org/serverless/runtimes/-/settings/repository
+```
Currently, only some project routes are placed under the `/-/` scope. However,
you can help us migrate more of them! To migrate project routes:
diff --git a/doc/development/sql.md b/doc/development/sql.md
index edeca7fb298..a256fd46c09 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -94,7 +94,9 @@ on the amount of data indexed).
To keep naming of these indexes consistent please use the following naming
pattern:
- index_TABLE_on_COLUMN_trigram
+```
+index_TABLE_on_COLUMN_trigram
+```
For example, a GIN/trigram index for `issues.title` would be called
`index_issues_on_title_trigram`.
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index 7a7fca46534..87d48726268 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -1,6 +1,6 @@
# GitLab tests in the Continuous Integration (CI) context
-### Test suite parallelization on the CI
+## Test suite parallelization on the CI
Our current CI parallelization setup is as follows:
@@ -26,7 +26,7 @@ Our current CI parallelization setup is as follows:
After that, the next pipeline will use the up-to-date
`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file.
-### Monitoring
+## Monitoring
The GitLab test suite is [monitored] for the `master` branch, and any branch
that includes `rspec-profile` in their name.
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 527cd350633..59eb3ecfd7e 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -79,7 +79,7 @@ subgraph gitlab-ce/ee pipeline
end
subgraph omnibus-gitlab pipeline
- A2[<b>`Trigger-docker` stage</b></b><br />`Trigger:gitlab-docker` job] -->|once done| B2
+ A2[<b>`Trigger-docker` stage</b><br />`Trigger:gitlab-docker` job] -->|once done| B2
end
subgraph gitlab-qa pipeline
diff --git a/doc/development/testing_guide/end_to_end/quick_start_guide.md b/doc/development/testing_guide/end_to_end/quick_start_guide.md
index 041bdf716b3..064fb0e31dd 100644
--- a/doc/development/testing_guide/end_to_end/quick_start_guide.md
+++ b/doc/development/testing_guide/end_to_end/quick_start_guide.md
@@ -394,15 +394,15 @@ end
By defining the `api_get_path` method, we allow the [`ApiFabricator`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa/resource/api_fabricator.rb) module to know which path to use to get a single issue.
-> This `GET` path can be found in the [public API documentation](https://docs.gitlab.com/ee/api/issues.html#single-issue).
+> This `GET` path can be found in the [public API documentation](../../../api/issues.md#single-issue).
By defining the `api_post_path` method, we allow the [`ApiFabricator`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/qa/qa/resource/api_fabricator.rb) module to know which path to use to create a new issue in a specific project.
-> This `POST` path can be found in the [public API documentation](https://docs.gitlab.com/ee/api/issues.html#new-issue).
+> This `POST` path can be found in the [public API documentation](../../../api/issues.md#new-issue).
By defining the `api_post_body` method, we allow the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request.
-> Notice that we pass both `labels` and `title` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](https://docs.gitlab.com/ee/api/issues.html#new-issue). Also, notice that we keep them alphabetically organized.
+> Notice that we pass both `labels` and `title` attributes in the `api_post_body`, where `labels` receives an array of labels, and [`title` is required](../../../api/issues.md#new-issue). Also, notice that we keep them alphabetically organized.
**Label resource**
@@ -441,7 +441,7 @@ By defining the `api_post_path` method, we allow for the [`ApiFabricator `](http
By defining the `api_post_body` method, we we allow for the [`ApiFabricator.api_post`](https://gitlab.com/gitlab-org/gitlab-ee/blob/a9177ca1812bac57e2b2fa4560e1d5dd8ffac38b/qa/qa/resource/api_fabricator.rb#L68) method to know which data to send when making the `POST` request.
-> Notice that we pass both `color` and `name` attributes in the `api_post_body` since [those are required](https://docs.gitlab.com/ee/api/labels.html#create-a-new-label). Also, notice that we keep them alphabetically organized.
+> Notice that we pass both `color` and `name` attributes in the `api_post_body` since [those are required](../../../api/labels.md#create-a-new-label). Also, notice that we keep them alphabetically organized.
### 8. Page Objects
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 28ebb6f0f64..98df0b5ea7c 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -560,7 +560,6 @@ end
[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
[rspec]: https://github.com/rspec/rspec-rails#feature-specs
[capybara]: https://github.com/teamcapybara/capybara
-[karma]: http://karma-runner.github.io/
[jasmine]: https://jasmine.github.io/
---
diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md
index 93ee2a6371a..aadbea1a540 100644
--- a/doc/development/testing_guide/index.md
+++ b/doc/development/testing_guide/index.md
@@ -11,7 +11,7 @@ importance.
## Overview
-GitLab is built on top of [Ruby on Rails][rails], and we're using [RSpec] for all
+GitLab is built on top of [Ruby on Rails](https://rubyonrails.org/), and we're using [RSpec] for all
the backend tests, with [Capybara] for end-to-end integration testing.
On the frontend side, we're using [Karma] and [Jasmine] for JavaScript unit and
integration testing.
@@ -80,9 +80,6 @@ Everything you should know about how to run end-to-end tests using
[Return to Development documentation](../README.md)
-[^1]: /ci/yaml/README.html#dependencies
-
-[rails]: http://rubyonrails.org/
[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
[Capybara]: https://github.com/teamcapybara/capybara
[Karma]: http://karma-runner.github.io/
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index bfbb7be70e3..11aafd7b639 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -654,7 +654,6 @@ and related tools such as:
- <https://explain.depesz.com/>
- <http://tatiyants.com/postgres-query-plan-visualization/>
-
## Producing query plans
There are a few ways to get the output of a query plan. Of course you
diff --git a/doc/development/ux_guide/resources.md b/doc/development/ux_guide/resources.md
index baec235a8dd..ae092246d05 100644
--- a/doc/development/ux_guide/resources.md
+++ b/doc/development/ux_guide/resources.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://design.gitlab.com/resources/design-resources'
+redirect_to: 'https://design.gitlab.com/resources/design-resources/'
---
-The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/).
+The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/resources/design-resources/).
diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md
index a187b3cbb07..a3f6f2b327c 100644
--- a/doc/downgrade_ee_to_ce/README.md
+++ b/doc/downgrade_ee_to_ce/README.md
@@ -81,7 +81,7 @@ To downgrade an Omnibus installation, it is sufficient to install the Community
Edition package on top of the currently installed one. You can do this manually,
by directly [downloading the package](https://packages.gitlab.com/gitlab/gitlab-ce)
you need, or by adding our CE package repository and following the
-[CE installation instructions](https://about.gitlab.com/installation/?version=ce).
+[CE installation instructions](https://about.gitlab.com/install/?version=ce).
**Source Installation**
diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md
index 0c268eff9f1..fd16047b8e4 100644
--- a/doc/gitlab-basics/README.md
+++ b/doc/gitlab-basics/README.md
@@ -22,7 +22,7 @@ The following are guides to basic GitLab functionality:
- [Fork a project](fork-project.md), to duplicate projects so they can be worked on in parallel.
- [Add a file](add-file.md), to add new files to a project's repository.
- [Add an image](add-image.md), to add new images to a project's repository.
-- [Create an issue](../user/project/issues/create_new_issue.md), to start collaborating within a project.
+- [Create an issue](../user/project/issues/managing_issues.md#create-a-new-issue), to start collaborating within a project.
- [Create a merge request](add-merge-request.md), to request changes made in a branch be merged into a project's repository.
- See how these features come together in the [GitLab Flow introduction video](https://youtu.be/InKNIvky2KE) and [GitLab Flow page](../workflow/gitlab_flow.md).
diff --git a/doc/gitlab-basics/create-issue.md b/doc/gitlab-basics/create-issue.md
index 6e2a09fc030..5fa5f1bf2e2 100644
--- a/doc/gitlab-basics/create-issue.md
+++ b/doc/gitlab-basics/create-issue.md
@@ -1,5 +1,5 @@
---
-redirect_to: '../user/project/issues/index.md#issue-actions'
+redirect_to: '../user/project/issues/index.md#viewing-and-managing-issues'
---
-This document was moved to [another location](../user/project/issues/index.md#issue-actions).
+This document was moved to [another location](../user/project/issues/index.md#viewing-and-managing-issues).
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index a9ae4fb23f9..ccba72f0ef8 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -16,7 +16,7 @@ To create a project in GitLab:
- [Import a project](../user/project/import/index.md) from a different repository,
if enabled on your GitLab instance. Contact your GitLab admin if this
is unavailable.
- - Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **[PREMIUM]**
+ - Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)**
## Blank projects
@@ -69,7 +69,7 @@ TIP: **Tip:**
You can improve the existing built-in templates or contribute new ones on the
[`project-templates`](https://gitlab.com/gitlab-org/project-templates) and [`pages`](https://gitlab.com/pages) groups.
-### Custom project templates **[PREMIUM ONLY]**
+### Custom project templates **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6860) in
[GitLab Premium](https://about.gitlab.com/pricing) 11.2.
diff --git a/doc/install/README.md b/doc/install/README.md
index 9cc21412898..af98791c8e9 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -4,7 +4,7 @@ description: Read through the GitLab installation methods.
type: index
---
-# Installation **[CORE ONLY]**
+# Installation **(CORE ONLY)**
GitLab can be installed in most GNU/Linux distributions and in a number
of cloud providers. To get the best experience from GitLab you need to balance
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index 77c61acbfd4..14bf7012c01 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -55,7 +55,7 @@ After a few seconds, the instance will be created and available to log in. The n
![GitLab first sign in](img/ssh_terminal.png)
-1. Next, follow the instructions for installing GitLab for the operating system you choose, at <https://about.gitlab.com/installation/>. You can use the IP address from the step above, as the hostname.
+1. Next, follow the instructions for installing GitLab for the operating system you choose, at <https://about.gitlab.com/install/>. You can use the IP address from the step above, as the hostname.
1. Congratulations! GitLab is now installed and you can access it via your browser. To finish installation, open the URL in your browser and provide the initial administrator password. The username for this account is `root`.
@@ -128,9 +128,9 @@ GitLab can be configured to authenticate with other OAuth providers, LDAP, SAML,
Kerberos, etc. Here are some documents you might be interested in reading:
- [Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/)
-- [Integration documentation](https://docs.gitlab.com/ce/integration/)
-- [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html)
-- [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html)
+- [Integration documentation](../../integration/README.md)
+- [GitLab Pages configuration](../../administration/pages/index.md)
+- [GitLab Container Registry configuration](../../administration/container_registry.md)
[freetrial]: https://console.cloud.google.com/freetrial "GCP free trial"
[ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses"
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 70e5ab28931..e9206469e5d 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -167,7 +167,7 @@ cd pcre2-10.33
chmod +x configure
./configure --prefix=/usr --enable-jit
make
-make install
+sudo make install
# Download and compile from source
cd /tmp
@@ -634,8 +634,8 @@ Gitaly must be running for the next section.
gitlab_path=/home/git/gitlab
gitaly_path=/home/git/gitaly
-sudo -u git -H $gitlab_path/bin/daemon_with_pidfile $gitlab_path/tmp/pids/gitaly.pid \
- $gitaly_path/gitaly $gitaly_path/config.toml >> $gitlab_path/log/gitaly.log 2>&1 &
+sudo -u git -H sh -c "$gitlab_path/bin/daemon_with_pidfile $gitlab_path/tmp/pids/gitaly.pid \
+ $gitaly_path/gitaly $gitaly_path/config.toml >> $gitlab_path/log/gitaly.log 2>&1 &"
```
### Initialize Database and Activate Advanced Features
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 45d07ec5d11..e4a2d9ecd68 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -13,8 +13,8 @@ for details.
## Introduction
-[OpenShift Origin][openshift] is an open source container application
-platform created by [RedHat], based on [kubernetes] and [Docker]. That means
+[OpenShift Origin](https://www.okd.io/) (**Note:** renamed to OKD in Aug 2018) is an open source container application
+platform created by [RedHat], based on [kubernetes](https://kubernetes.io/) and [Docker]. That means
you can host your own PaaS for free and almost with no hassle.
In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's
@@ -27,8 +27,11 @@ For a video demonstration on installing GitLab on OpenShift, check the article [
## Prerequisites
-OpenShift 3 is not yet deployed on RedHat's offered Online platform ([openshift.com]),
-so in order to test it, we will use an [all-in-one Virtualbox image][vm] that is
+CAUTION: **Caution:** This information is no longer up to date, as the current versions
+have changed and products have been renamed.
+
+OpenShift 3 is not yet deployed on RedHat's offered Online platform, [openshift.com](https://www.openshift.com/),
+so in order to test it, we will use an [all-in-one Virtualbox image](https://www.okd.io/minishift/) that is
offered by the OpenShift developers and managed by Vagrant. If you haven't done
already, go ahead and install the following components as they are essential to
test OpenShift easily:
@@ -458,7 +461,7 @@ OpenShift's website about [autoscaling].
## Current limitations
-As stated in the [all-in-one VM][vm] page:
+As stated in the [all-in-one VM](https://www.okd.io/minishift/) page:
> By default, OpenShift will not allow a container to run as root or even a
non-random container assigned userid. Most Docker images in the Dockerhub do not
@@ -506,12 +509,8 @@ is capable of. As always, you can refer to the detailed
PaaS and managing your applications with the ease of containers.
[RedHat]: https://www.redhat.com/en "RedHat website"
-[openshift]: https://www.openshift.org "OpenShift Origin website"
-[vm]: https://www.openshift.org/vm/ "OpenShift All-in-one VM"
[vm-new]: https://app.vagrantup.com/openshift/boxes/origin-all-in-one "Official OpenShift Vagrant box on Vagrant Cloud"
[template]: https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/docker/openshift-template.json "OpenShift template for GitLab"
-[openshift.com]: https://openshift.com "OpenShift Online"
-[kubernetes]: http://kubernetes.io/ "Kubernetes website"
[Docker]: https://www.docker.com "Docker website"
[oc]: https://docs.openshift.org/latest/cli_reference/get_started_cli.html "Documentation - oc CLI documentation"
[VirtualBox]: https://www.virtualbox.org/wiki/Downloads "VirtualBox downloads"
diff --git a/doc/install/pivotal/index.md b/doc/install/pivotal/index.md
index f068572f1e9..6a4b361c842 100644
--- a/doc/install/pivotal/index.md
+++ b/doc/install/pivotal/index.md
@@ -1,4 +1,4 @@
-# GitLab Pivotal Tile **[PREMIUM ONLY]**
+# GitLab Pivotal Tile **(PREMIUM ONLY)**
CAUTION: **Discontinued:**
As of September 13, 2017, the GitLab Enterprise Plus for Pivotal Cloud Foundry
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 68c1bcbc801..25ab608de3a 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -30,7 +30,7 @@ For the installations options, see [the main installation page](README.md).
- macOS
Installation of GitLab on these operating systems is possible, but not supported.
-Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/installation/) for more information.
+Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information.
### Microsoft Windows
diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md
index 4f7be70baf2..cb8f25d2895 100644
--- a/doc/integration/akismet.md
+++ b/doc/integration/akismet.md
@@ -3,7 +3,7 @@
> *Note:* Before 8.11 only issues submitted via the API and for non-project
members were submitted to Akismet.
-GitLab leverages [Akismet](http://akismet.com) to protect against spam. Currently
+GitLab leverages [Akismet](https://akismet.com/) to protect against spam. Currently
GitLab uses Akismet to prevent the creation of spam issues on public projects. Issues
created via the WebUI or the API can be submitted to Akismet for review.
diff --git a/doc/integration/azure.md b/doc/integration/azure.md
index 7a6d4bb143f..a9468f201ef 100644
--- a/doc/integration/azure.md
+++ b/doc/integration/azure.md
@@ -2,21 +2,21 @@
To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your application with Azure. Azure will generate a client ID and secret key for you to use.
-1. Sign in to the [Azure Management Portal](https://manage.windowsazure.com).
+1. Sign in to the [Azure Management Portal](https://portal.azure.com).
-1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab.
+1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab.
-1. Select "Applications" at the top bar and click the "Add" button the bottom.
+1. Select "Applications" at the top bar and click the "Add" button the bottom.
-1. Select "Add an application my organization is developing".
+1. Select "Add an application my organization is developing".
-1. Provide the project information and click the "Next" button.
- - Name: 'GitLab' works just fine here.
- - Type: 'WEB APPLICATION AND/OR WEB API'
+1. Provide the project information and click the "Next" button.
+ - Name: 'GitLab' works just fine here.
+ - Type: 'WEB APPLICATION AND/OR WEB API'
-1. On the "App properties" page enter the needed URI's and click the "Complete" button.
- - SIGN-IN URL: Enter the URL of your GitLab installation (e.g `https://gitlab.mycompany.com/`)
- - APP ID URI: Enter the endpoint URL for Microsoft to use, just has to be unique (e.g `https://mycompany.onmicrosoft.com/gitlab`)
+1. On the "App properties" page enter the needed URI's and click the "Complete" button.
+ - SIGN-IN URL: Enter the URL of your GitLab installation (e.g `https://gitlab.mycompany.com/`)
+ - APP ID URI: Enter the endpoint URL for Microsoft to use, just has to be unique (e.g `https://mycompany.onmicrosoft.com/gitlab`)
1. Select "Configure" in the top menu.
@@ -30,59 +30,59 @@ To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your ap
1. You will see lots of endpoint URLs in the form `https://login.microsoftonline.com/TENANT ID/...`, note down the TENANT ID part of one of those endpoints.
-1. On your GitLab server, open the configuration file.
+1. On your GitLab server, open the configuration file.
- For omnibus package:
+ For omnibus package:
- ```sh
- sudo editor /etc/gitlab/gitlab.rb
- ```
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
- For installations from source:
+ For installations from source:
- ```sh
- cd /home/git/gitlab
+ ```sh
+ cd /home/git/gitlab
- sudo -u git -H editor config/gitlab.yml
- ```
+ sudo -u git -H editor config/gitlab.yml
+ ```
-1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
-1. Add the provider configuration:
+1. Add the provider configuration:
- For omnibus package:
+ For omnibus package:
- ```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- "name" => "azure_oauth2",
- "args" => {
- "client_id" => "CLIENT ID",
- "client_secret" => "CLIENT SECRET",
- "tenant_id" => "TENANT ID",
- }
- }
- ]
- ```
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ "name" => "azure_oauth2",
+ "args" => {
+ "client_id" => "CLIENT ID",
+ "client_secret" => "CLIENT SECRET",
+ "tenant_id" => "TENANT ID",
+ }
+ }
+ ]
+ ```
- For installations from source:
+ For installations from source:
- ```
- - { name: 'azure_oauth2',
- args: { client_id: "CLIENT ID",
- client_secret: "CLIENT SECRET",
- tenant_id: "TENANT ID" } }
- ```
+ ```
+ - { name: 'azure_oauth2',
+ args: { client_id: "CLIENT ID",
+ client_secret: "CLIENT SECRET",
+ tenant_id: "TENANT ID" } }
+ ```
- The `base_azure_url` is optional and can be added for different locales;
- e.g. `base_azure_url: "https://login.microsoftonline.de"`.
+ The `base_azure_url` is optional and can be added for different locales;
+ e.g. `base_azure_url: "https://login.microsoftonline.de"`.
-1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above.
+1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above.
-1. Save the configuration file.
+1. Save the configuration file.
-1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
+ installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be a Microsoft icon below the regular sign in form. Click the icon to begin the authentication process. Microsoft will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 877330b8c44..da1df07a75d 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -1,4 +1,4 @@
-# Elasticsearch integration **[STARTER ONLY]**
+# Elasticsearch integration **(STARTER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/109 "Elasticsearch Merge Request") in GitLab [Starter](https://about.gitlab.com/pricing/) 8.4. Support
> for [Amazon Elasticsearch](http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-gsg.html) was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1305) in GitLab
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index e6496ae3a2e..50cb3d50009 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -1,4 +1,4 @@
-# Jenkins CI service **[STARTER]**
+# Jenkins CI service **(STARTER)**
>**Note:**
In GitLab 8.3, Jenkins integration using the
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 703736eeb3c..60c7bdabf93 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -1,4 +1,4 @@
-# GitLab Jira development panel integration **[PREMIUM]**
+# GitLab Jira development panel integration **(PREMIUM)**
> [Introduced][ee-2381] in [GitLab Premium][eep] 10.0.
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index 44117755b83..cf2ef511264 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -1,4 +1,4 @@
-# Kerberos integration **[STARTER ONLY]**
+# Kerberos integration **(STARTER ONLY)**
GitLab can integrate with [Kerberos][kerb] as an authentication mechanism.
diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md
index 3e72589ce12..3c1a0f2a117 100644
--- a/doc/integration/oauth2_generic.md
+++ b/doc/integration/oauth2_generic.md
@@ -1,7 +1,7 @@
# Sign into GitLab with (almost) any OAuth2 provider
The `omniauth-oauth2-generic` gem allows Single Sign On between GitLab and your own OAuth2 provider
-(or any OAuth2 provider compatible with this gem)
+(or any OAuth2 provider compatible with this gem)
This strategy is designed to allow configuration of the simple OmniAuth SSO process outlined below:
@@ -12,7 +12,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
1. Strategy parses user information from the response, using a **configurable** format
1. GitLab finds or creates the returned user and logs them in
-### Limitations of this Strategy:
+## Limitations of this Strategy:
- It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider
(importing projects or users, etc)
@@ -20,7 +20,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
- It is not able to fetch user information from more than one URL
- It has not been tested with user information formats other than JSON
-### Config Instructions
+## Config Instructions
1. Register your application in the OAuth2 provider you wish to authenticate with.
@@ -57,9 +57,9 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc
1. Restart GitLab for the changes to take effect
-On the sign in page there should now be a new button below the regular sign in form.
+On the sign in page there should now be a new button below the regular sign in form.
Click the button to begin your provider's authentication process. This will direct
the browser to your OAuth2 Provider's authentication page. If everything goes well
the user will be returned to your GitLab instance and will be signed in.
-[1]: https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example \ No newline at end of file
+[1]: https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index c02a29dffb4..b9dc2e123c5 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -11,7 +11,7 @@ If you want to use:
## Introduction to OAuth
-[OAuth] provides to client applications a 'secure delegated access' to server
+[OAuth](https://oauth.net/2/) provides to client applications a 'secure delegated access' to server
resources on behalf of a resource owner. In fact, OAuth allows an authorization
server to issue access tokens to third-party clients with the approval of the
resource owner, or the end-user.
@@ -85,5 +85,3 @@ application can perform such as `read_user` and `api`. There are many more scope
available.
At any time you can revoke any access by just clicking **Revoke**.
-
-[oauth]: http://oauth.net/2/ "OAuth website"
diff --git a/doc/integration/openid_connect_provider.md b/doc/integration/openid_connect_provider.md
index a7f907254a1..89f4924d717 100644
--- a/doc/integration/openid_connect_provider.md
+++ b/doc/integration/openid_connect_provider.md
@@ -5,7 +5,7 @@ to sign in to other services.
## Introduction to OpenID Connect
-[OpenID Connect] \(OIDC) is a simple identity layer on top of the
+[OpenID Connect](https://openid.net/connect/) \(OIDC) is a simple identity layer on top of the
OAuth 2.0 protocol. It allows clients to verify the identity of the end-user
based on the authentication performed by GitLab, as well as to obtain
basic profile information about the end-user in an interoperable and
@@ -14,7 +14,7 @@ but does so in a way that is API-friendly, and usable by native and
mobile applications.
On the client side, you can use [omniauth-openid-connect] for Rails
-applications, or any of the other available [client implementations].
+applications, or any of the other available [client implementations](https://openid.net/developers/libraries/#connect).
GitLab's implementation uses the [doorkeeper-openid_connect] gem, refer
to its README for more details about which parts of the specifications
@@ -46,8 +46,6 @@ Currently the following user information is shared with clients:
Only the `sub` and `sub_legacy` claims are included in the ID token, all other claims are available from the `/oauth/userinfo` endpoint used by OIDC clients.
-[OpenID Connect]: http://openid.net/connect/ "OpenID Connect website"
[doorkeeper-openid_connect]: https://github.com/doorkeeper-gem/doorkeeper-openid_connect "Doorkeeper::OpenidConnect website"
[OAuth guide]: oauth_provider.md "GitLab as OAuth2 authentication service provider"
[omniauth-openid-connect]: https://github.com/jjbohn/omniauth-openid-connect/ "OmniAuth::OpenIDConnect website"
-[client implementations]: http://openid.net/developers/libraries#connect "List of available client implementations"
diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md
index 616f3a76b2c..07c83c1a049 100644
--- a/doc/integration/shibboleth.md
+++ b/doc/integration/shibboleth.md
@@ -5,8 +5,8 @@ This documentation is for enabling shibboleth with omnibus-gitlab package.
In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however this is difficult to configure using the bundled Nginx provided in the omnibus-gitlab package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider.
To enable the Shibboleth OmniAuth provider you must configure Apache shibboleth module.
-Installation and configuration of module it self is out of scope of this document.
-Check <https://wiki.shibboleth.net/> for more info.
+The installation and configuration of the module itself is out of the scope of this document.
+Check <https://wiki.shibboleth.net/confluence/display/SP3/Apache> for more info.
You can find Apache config in gitlab-recipes (<https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache>).
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index 1cbfd81dfa9..d8096993885 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -2,80 +2,81 @@
To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use.
-1. Sign in to [Twitter Application Management](https://apps.twitter.com/).
+1. Sign in to [Twitter Application Management](https://developer.twitter.com/apps).
-1. Select "Create new app"
+1. Select "Create new app"
-1. Fill in the application details.
- - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or
- something else descriptive.
- - Description: Create a description.
- - Website: The URL to your GitLab installation. `https://gitlab.example.com`
- - Callback URL: `https://gitlab.example.com/users/auth/twitter/callback`
- - Agree to the "Developer Agreement".
+1. Fill in the application details.
+ - Name: This can be anything. Consider something like `<Organization>'s GitLab` or `<Your Name>'s GitLab` or
+ something else descriptive.
+ - Description: Create a description.
+ - Website: The URL to your GitLab installation. `https://gitlab.example.com`
+ - Callback URL: `https://gitlab.example.com/users/auth/twitter/callback`
+ - Agree to the "Developer Agreement".
- ![Twitter App Details](img/twitter_app_details.png)
-1. Select "Create your Twitter application."
+ ![Twitter App Details](img/twitter_app_details.png)
-1. Select the "Settings" tab.
+1. Select "Create your Twitter application."
-1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter."
+1. Select the "Settings" tab.
-1. Select "Update settings" at the bottom to save changes.
+1. Underneath the Callback URL check the box next to "Allow this application to be used to Sign in with Twitter."
-1. Select the "Keys and Access Tokens" tab.
+1. Select "Update settings" at the bottom to save changes.
-1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration.
+1. Select the "Keys and Access Tokens" tab.
- ![Twitter app](img/twitter_app_api_keys.png)
+1. You should now see an API key and API secret (see screenshot). Keep this page open as you continue configuration.
-1. On your GitLab server, open the configuration file.
+ ![Twitter app](img/twitter_app_api_keys.png)
- For omnibus package:
+1. On your GitLab server, open the configuration file.
- ```sh
- sudo editor /etc/gitlab/gitlab.rb
- ```
+ For omnibus package:
- For installations from source:
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
- ```sh
- cd /home/git/gitlab
+ For installations from source:
- sudo -u git -H editor config/gitlab.yml
- ```
+ ```sh
+ cd /home/git/gitlab
-1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+ sudo -u git -H editor config/gitlab.yml
+ ```
-1. Add the provider configuration:
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
- For omnibus package:
+1. Add the provider configuration:
- ```ruby
- gitlab_rails['omniauth_providers'] = [
- {
- "name" => "twitter",
- "app_id" => "YOUR_APP_ID",
- "app_secret" => "YOUR_APP_SECRET"
- }
- ]
- ```
+ For omnibus package:
- For installations from source:
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ "name" => "twitter",
+ "app_id" => "YOUR_APP_ID",
+ "app_secret" => "YOUR_APP_SECRET"
+ }
+ ]
+ ```
- ```
- - { name: 'twitter', app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET' }
- ```
+ For installations from source:
-1. Change 'YOUR_APP_ID' to the API key from Twitter page in step 11.
+ ```
+ - { name: 'twitter', app_id: 'YOUR_APP_ID',
+ app_secret: 'YOUR_APP_SECRET' }
+ ```
-1. Change 'YOUR_APP_SECRET' to the API secret from the Twitter page in step 11.
+1. Change 'YOUR_APP_ID' to the API key from Twitter page in step 11.
-1. Save the configuration file.
+1. Change 'YOUR_APP_SECRET' to the API secret from the Twitter page in step 11.
-1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+1. Save the configuration file.
+
+1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
+ installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be a Twitter icon below the regular sign in form. Click the icon to begin the authentication process. Twitter will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
diff --git a/doc/intro/README.md b/doc/intro/README.md
index d9c733d4285..33b23372280 100644
--- a/doc/intro/README.md
+++ b/doc/intro/README.md
@@ -15,7 +15,7 @@ Create projects and groups.
Create issues, labels, milestones, cast your vote, and review issues.
-- [Create an issue](../user/project/issues/create_new_issue.md)
+- [Create an issue](../user/project/issues/managing_issues.md#create-a-new-issue)
- [Assign labels to issues](../user/project/labels.md)
- [Use milestones as an overview of your project's tracker](../user/project/milestones/index.md)
- [Use voting to express your like/dislike to issues and merge requests](../workflow/award_emoji.md)
@@ -26,7 +26,7 @@ Create merge requests and review code.
- [Fork a project and contribute to it](../workflow/forking_workflow.md)
- [Create a new merge request](../gitlab-basics/add-merge-request.md)
-- [Automatically close issues from merge requests](../user/project/issues/automatic_issue_closing.md)
+- [Automatically close issues from merge requests](../user/project/issues/managing_issues.md#closing-issues-automatically)
- [Automatically merge when pipeline succeeds](../user/project/merge_requests/merge_when_pipeline_succeeds.md)
- [Revert any commit](../user/project/merge_requests/revert_changes.md)
- [Cherry-pick any commit](../user/project/merge_requests/cherry_pick_changes.md)
@@ -41,6 +41,6 @@ Use the built-in continuous integration in GitLab.
Install and update your GitLab installation.
-- [Install GitLab](https://about.gitlab.com/installation/)
+- [Install GitLab](https://about.gitlab.com/install/)
- [Update GitLab](https://about.gitlab.com/update/)
- [Explore Omnibus GitLab configuration options](https://docs.gitlab.com/omnibus/settings/configuration.html)
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index 72bace3d282..018c273c51a 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -9,7 +9,7 @@ patch and security releases. New releases are usually announced on the [GitLab b
## Versioning
-GitLab uses [Semantic Versioning](http://semver.org/) for its releases:
+GitLab uses [Semantic Versioning](https://semver.org/) for its releases:
`(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e).
For example, for GitLab version 10.5.7:
diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md
index 2142f5a5f69..b1754131e76 100644
--- a/doc/push_rules/push_rules.md
+++ b/doc/push_rules/push_rules.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# Push Rules **[STARTER]**
+# Push Rules **(STARTER)**
Gain additional control over what can and can't be pushed to your repository by using
regular expressions to reject pushes based on commit contents, branch names or file details.
diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md
index 0729875daf8..dcc96507676 100644
--- a/doc/raketasks/README.md
+++ b/doc/raketasks/README.md
@@ -9,10 +9,10 @@ comments: false
- [Cleanup](cleanup.md)
- [Features](features.md)
- [LDAP Maintenance](../administration/raketasks/ldap.md)
-- [General Maintenance](maintenance.md) and self-checks
+- [General Maintenance](../administration/raketasks/maintenance.md) and self-checks
- [User management](user_management.md)
- [Webhooks](web_hooks.md)
- [Import](import.md) of git repositories in bulk
-- [Rebuild authorized_keys file](http://docs.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators
+- [Rebuild authorized_keys file](../administration/raketasks/maintenance.md#rebuild-authorized_keys-file) task for administrators
- [Migrate Uploads](../administration/raketasks/uploads/migrate.md)
- [Sanitize Uploads](../administration/raketasks/uploads/sanitize.md)
diff --git a/doc/raketasks/web_hooks.md b/doc/raketasks/web_hooks.md
index 2c6ae0749dd..a498e9793c1 100644
--- a/doc/raketasks/web_hooks.md
+++ b/doc/raketasks/web_hooks.md
@@ -1,4 +1,4 @@
-# Webhooks administration **[CORE ONLY]**
+# Webhooks administration **(CORE ONLY)**
## Add a webhook for **ALL** projects:
diff --git a/doc/tools/email.md b/doc/tools/email.md
index a2d677484f0..72a5d094bc9 100644
--- a/doc/tools/email.md
+++ b/doc/tools/email.md
@@ -2,7 +2,7 @@
type: howto, reference
---
-# Email from GitLab **[STARTER ONLY]**
+# Email from GitLab **(STARTER ONLY)**
GitLab provides a simple tool to administrators for emailing all users, or users of
a chosen group or project, right from the admin area. Users will receive the email
diff --git a/doc/topics/application_development_platform/index.md b/doc/topics/application_development_platform/index.md
index 8742606479d..2ea561eb943 100644
--- a/doc/topics/application_development_platform/index.md
+++ b/doc/topics/application_development_platform/index.md
@@ -9,10 +9,10 @@ The GitLab Application Development Platform aims to:
- Reduce and even eliminate the time it takes for an Operations team
to provide a full environment for software developers.
-- Get developers up and running fast so they can focus on writing
+- Get developers up and running fast so they can focus on writing
great applications with a robust development feature set.
-- Provide best-of-breed security features so that applications developed
- with GitLab are not affected by vulnerabilities that may lead to security
+- Provide best-of-breed security features so that applications developed
+ with GitLab are not affected by vulnerabilities that may lead to security
problems and unintended use.
It is comprised of the following high-level elements:
@@ -35,28 +35,28 @@ with various cloud providers.
### Build, test, deploy
In order to provide modern DevOps workflows, our Application Development Platform will rely on
-[Auto DevOps](https://docs.gitlab.com/ee/topics/autodevops/) to provide those workflows. Auto DevOps works with
-any Kubernetes cluster; you're not limited to running on GitLab's infrastructure. Additionally, Auto DevOps offers
-an incremental consumption path. Because it is [composable](https://docs.gitlab.com/ee/topics/autodevops/#using-components-of-auto-devops),
+[Auto DevOps](../autodevops/index.md) to provide those workflows. Auto DevOps works with
+any Kubernetes cluster; you're not limited to running on GitLab's infrastructure. Additionally, Auto DevOps offers
+an incremental consumption path. Because it is [composable](../autodevops/index.md#using-components-of-auto-devops),
you can use as much or as little of the default pipeline as you'd like, and deeply customize without having to integrate a completely different platform.
### Security
-The Application Development Platform helps you ensure that the applications you create are not affected by vulnerabilities
+The Application Development Platform helps you ensure that the applications you create are not affected by vulnerabilities
that may lead to security problems and unintended use. This can be achieved by making use of the embedded security features of Auto DevOps,
-which inform security teams and developers if there is something to consider changing in their apps
+which inform security teams and developers if there is something to consider changing in their apps
before it is too late to create a preventative fix. The following features are included:
-- [Auto SAST (Static Application Security Testing)](https://docs.gitlab.com/ee/topics/autodevops/#auto-sast-ultimate)
-- [Auto Dependency Scanning](https://docs.gitlab.com/ee/topics/autodevops/#auto-dependency-scanning-ultimate)
-- [Auto Container Scanning](https://docs.gitlab.com/ee/topics/autodevops/#auto-container-scanning-ultimate)
-- [Auto DAST (Dynamic Application Security Testing)](https://docs.gitlab.com/ee/topics/autodevops/#auto-dast-ultimate)
+- [Auto SAST (Static Application Security Testing)](../autodevops/index.md#auto-sast-ultimate)
+- [Auto Dependency Scanning](../autodevops/index.md#auto-dependency-scanning-ultimate)
+- [Auto Container Scanning](../autodevops/index.md#auto-container-scanning-ultimate)
+- [Auto DAST (Dynamic Application Security Testing)](../autodevops/index.md#auto-dast-ultimate)
### Observability
Performance is a critical aspect of the user experience, and ensuring your application is responsive and available is everyone's
-responsibility. The Application Development Platform integrates key performance analytics and feedback
+responsibility. The Application Development Platform integrates key performance analytics and feedback
into GitLab, automatically. The following features are included:
-- [Auto Monitoring](https://docs.gitlab.com/ee/topics/autodevops/#auto-monitoring)
-- [In-app Kubernetes Pod Logs](https://docs.gitlab.com/ee/user/project/clusters/kubernetes_pod_logs.html) \ No newline at end of file
+- [Auto Monitoring](../autodevops/index.md#auto-monitoring)
+- [In-app Kubernetes Pod Logs](../../user/project/clusters/kubernetes_pod_logs.md)
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index 228da2d1f57..8b4a2f1630b 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -17,11 +17,11 @@ This page gathers all the resources for the topic **Authentication** within GitL
## GitLab administrators
- [LDAP (Community Edition)](../../administration/auth/ldap.md)
-- [LDAP (Enterprise Edition)](../../administration/auth/ldap-ee.md) **[STARTER]**
+- [LDAP (Enterprise Edition)](../../administration/auth/ldap-ee.md) **(STARTER)**
- [Enforce Two-factor Authentication (2FA)](../../security/two_factor_authentication.md#enforce-two-factor-authentication-2fa)
- **Articles:**
- [How to Configure LDAP with GitLab CE](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md)
- - [How to Configure LDAP with GitLab EE](../../administration/auth/how_to_configure_ldap_gitlab_ee/index.md) **[STARTER]**
+ - [How to Configure LDAP with GitLab EE](../../administration/auth/how_to_configure_ldap_gitlab_ee/index.md) **(STARTER)**
- [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
- [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/support-engineering/ldap/debugging_ldap.html)
- **Integrations:**
@@ -30,10 +30,10 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [Atlassian Crowd OmniAuth Provider](../../administration/auth/crowd.md)
- [CAS OmniAuth Provider](../../integration/cas.md)
- [SAML OmniAuth Provider](../../integration/saml.md)
- - [SAML for GitLab.com Groups](../../user/group/saml_sso/index.md) **[SILVER ONLY]**
- - [SCIM user provisioning for GitLab.com Groups](../../user/group/saml_sso/scim_setup.md) **[SILVER ONLY]**
+ - [SAML for GitLab.com Groups](../../user/group/saml_sso/index.md) **(SILVER ONLY)**
+ - [SCIM user provisioning for GitLab.com Groups](../../user/group/saml_sso/scim_setup.md) **(SILVER ONLY)**
- [Okta SSO provider](../../administration/auth/okta.md)
- - [Kerberos integration (GitLab EE)](../../integration/kerberos.md) **[STARTER]**
+ - [Kerberos integration (GitLab EE)](../../integration/kerberos.md) **(STARTER)**
## API
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index bd788cb138c..e2d51f673b5 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -61,15 +61,15 @@ project in a simple and automatic way:
1. [Auto Build](#auto-build)
1. [Auto Test](#auto-test)
-1. [Auto Code Quality](#auto-code-quality-starter) **[STARTER]**
-1. [Auto SAST (Static Application Security Testing)](#auto-sast-ultimate) **[ULTIMATE]**
-1. [Auto Dependency Scanning](#auto-dependency-scanning-ultimate) **[ULTIMATE]**
-1. [Auto License Management](#auto-license-management-ultimate) **[ULTIMATE]**
-1. [Auto Container Scanning](#auto-container-scanning-ultimate) **[ULTIMATE]**
+1. [Auto Code Quality](#auto-code-quality-starter) **(STARTER)**
+1. [Auto SAST (Static Application Security Testing)](#auto-sast-ultimate) **(ULTIMATE)**
+1. [Auto Dependency Scanning](#auto-dependency-scanning-ultimate) **(ULTIMATE)**
+1. [Auto License Management](#auto-license-management-ultimate) **(ULTIMATE)**
+1. [Auto Container Scanning](#auto-container-scanning-ultimate) **(ULTIMATE)**
1. [Auto Review Apps](#auto-review-apps)
-1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast-ultimate) **[ULTIMATE]**
+1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast-ultimate) **(ULTIMATE)**
1. [Auto Deploy](#auto-deploy)
-1. [Auto Browser Performance Testing](#auto-browser-performance-testing-premium) **[PREMIUM]**
+1. [Auto Browser Performance Testing](#auto-browser-performance-testing-premium) **(PREMIUM)**
1. [Auto Monitoring](#auto-monitoring)
As Auto DevOps relies on many different components, it's good to have a basic
@@ -169,7 +169,7 @@ Support for `AUTO_DEVOPS_DOMAIN` was [removed in GitLab
12.0](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959).
-## Using multiple Kubernetes clusters **[PREMIUM]**
+## Using multiple Kubernetes clusters **(PREMIUM)**
When using Auto DevOps, you may want to deploy different environments to
different Kubernetes clusters. This is possible due to the 1:1 connection that
@@ -354,7 +354,7 @@ you may succeed with a [custom buildpack](#custom-buildpacks). Check the
Auto Test uses tests you already have in your application. If there are no
tests, it's up to you to add them.
-### Auto Code Quality **[STARTER]**
+### Auto Code Quality **(STARTER)**
Auto Code Quality uses the
[Code Quality image](https://gitlab.com/gitlab-org/security-products/codequality) to run
@@ -365,7 +365,7 @@ out.
Any differences between the source and target branches are also
[shown in the merge request widget](../../user/project/merge_requests/code_quality.md).
-### Auto SAST **[ULTIMATE]**
+### Auto SAST **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 10.3.
@@ -380,7 +380,7 @@ check out.
Any security warnings are also shown in the merge request widget. Read more how
[SAST works](../../user/application_security/sast/index.md).
-### Auto Dependency Scanning **[ULTIMATE]**
+### Auto Dependency Scanning **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 10.7.
@@ -397,7 +397,7 @@ check out.
Any security warnings are also shown in the merge request widget. Read more about
[Dependency Scanning](../../user/application_security/dependency_scanning/index.md).
-### Auto License Management **[ULTIMATE]**
+### Auto License Management **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 11.0.
@@ -413,7 +413,7 @@ check out.
Any licenses are also shown in the merge request widget. Read more how
[License Management works](../../user/application_security/license_management/index.md).
-### Auto Container Scanning **[ULTIMATE]**
+### Auto Container Scanning **(ULTIMATE)**
> Introduced in GitLab 10.4.
@@ -468,7 +468,7 @@ deploys with Auto DevOps can undo your changes. Also, if you change something
and want to undo it by deploying again, Helm may not detect that anything changed
in the first place, and thus not realize that it needs to re-apply the old config.
-### Auto DAST **[ULTIMATE]**
+### Auto DAST **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 10.4.
@@ -483,7 +483,7 @@ later download and check out.
Any security warnings are also shown in the merge request widget. Read how
[DAST works](../../user/application_security/dast/index.md).
-### Auto Browser Performance Testing **[PREMIUM]**
+### Auto Browser Performance Testing **(PREMIUM)**
> Introduced in [GitLab Premium][ee] 10.4.
@@ -655,7 +655,7 @@ repo or by specifying a project variable:
- **Project variable** - Create a [project variable](../../ci/variables/README.md#gitlab-cicd-environment-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use or create two project variables `AUTO_DEVOPS_CHART_REPOSITORY` with the URL of a custom chart repository and `AUTO_DEVOPS_CHART` with the path to the chart.
-### Custom Helm chart per environment **[PREMIUM]**
+### Custom Helm chart per environment **(PREMIUM)**
You can specify the use of a custom Helm chart per environment by scoping the environment variable
to the desired environment. See [Limiting environment scopes of variables](../../ci/variables/README.md#limiting-environment-scopes-of-environment-variables-premium).
@@ -903,7 +903,7 @@ If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to
to a `staging` environment, and a `production_manual` job will be created for
you when you're ready to manually deploy to production.
-#### Deploy policy for canary environments **[PREMIUM]**
+#### Deploy policy for canary environments **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/171)
in GitLab 11.0.
@@ -918,7 +918,7 @@ If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
- `production_manual` which is to be used by you when you're ready to manually
deploy to production.
-#### Incremental rollout to production **[PREMIUM]**
+#### Incremental rollout to production **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8.
@@ -976,7 +976,7 @@ Before GitLab 11.4 this feature was enabled by the presence of the
`INCREMENTAL_ROLLOUT_ENABLED` environment variable.
This configuration is deprecated and will be removed in the future.
-#### Timed incremental rollout to production **[PREMIUM]**
+#### Timed incremental rollout to production **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7545) in GitLab 11.4.
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 6717e95266e..c1771a57da0 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -159,15 +159,15 @@ In the **test** stage, GitLab runs various checks on the application:
- The `test` job runs unit and integration tests by detecting the language and
framework ([Auto Test](index.md#auto-test))
- The `code_quality` job checks the code quality and is allowed to fail
- ([Auto Code Quality](index.md#auto-code-quality-starter)) **[STARTER]**
+ ([Auto Code Quality](index.md#auto-code-quality-starter)) **(STARTER)**
- The `container_scanning` job checks the Docker container if it has any
vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning-ultimate))
- The `dependency_scanning` job checks if the application has any dependencies
- susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **[ULTIMATE]**
+ susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **(ULTIMATE)**
- The `sast` job runs static analysis on the current code to check for potential
- security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **[ULTIMATE]**
+ security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **(ULTIMATE)**
- The `license_management` job searches the application's dependencies to determine each of their
- licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **[ULTIMATE]**
+ licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **(ULTIMATE)**
NOTE: **Note:**
As you might have noticed, all jobs except `test` are allowed to fail in the
@@ -178,7 +178,7 @@ deploys the application in Kubernetes ([Auto Deploy](index.md#auto-deploy)).
Lastly, in the **performance** stage, some performance tests will run
on the deployed application
-([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **[PREMIUM]**
+([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **(PREMIUM)**
---
@@ -285,8 +285,8 @@ all within GitLab. Despite its automatic nature, Auto DevOps can also be configu
and customized to fit your workflow. Here are some helpful resources for further reading:
1. [Auto DevOps](index.md)
-1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **[PREMIUM]**
-1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **[PREMIUM]**
+1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **(PREMIUM)**
+1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **(PREMIUM)**
1. [Disable jobs you don't need with environment variables](index.md#environment-variables)
1. [Use a static IP for your cluster](../../user/project/clusters/index.md#using-a-static-ip)
1. [Use your own buildpacks to build your application](index.md#custom-buildpacks)
diff --git a/doc/university/README.md b/doc/university/README.md
index 9d861460618..f696db2df20 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -73,7 +73,7 @@ The GitLab University curriculum is composed of GitLab videos, screencasts, pres
- Being part of our Great Community and Contributing to GitLab
1. [Getting Started with the GitLab Development Kit (GDK)](https://about.gitlab.com/2016/06/08/getting-started-with-gitlab-development-kit/)
1. [Contributing Technical Articles to the GitLab Blog](https://about.gitlab.com/2016/01/26/call-for-writers/)
-1. [GitLab Training Workshops](https://docs.gitlab.com/ce/university/training/end-user/)
+1. [GitLab Training Workshops](training/end-user/README.md)
1. [GitLab Professional Services](https://about.gitlab.com/services/)
### 1.8 GitLab Training Material
diff --git a/doc/university/support/README.md b/doc/university/support/README.md
index 2c6e52acfde..fdeba89f9c8 100644
--- a/doc/university/support/README.md
+++ b/doc/university/support/README.md
@@ -45,7 +45,7 @@ It's important to understand how to install GitLab in the same way that our user
Sometimes we need to upgrade customers from old versions of GitLab to latest, so it's good to get some experience of doing that now.
-- [Installation Methods](https://about.gitlab.com/installation/):
+- [Installation Methods](https://about.gitlab.com/install/):
- [Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/)
- [Docker](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/docker)
- [Source](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md)
diff --git a/doc/university/training/index.md b/doc/university/training/index.md
index 4c8ae0d9ce8..61fde9d8336 100644
--- a/doc/university/training/index.md
+++ b/doc/university/training/index.md
@@ -6,7 +6,7 @@ type: index
# GitLab Training Material
All GitLab training material is stored in markdown format. Slides are
-generated using [Deskset](http://www.decksetapp.com/).
+generated using [Deskset](https://www.deckset.com/).
All training material is open to public contribution.
@@ -35,8 +35,8 @@ This section contains the following topics:
## Additional Resources
1. [GitLab Documentation](https://docs.gitlab.com)
-1. [GUI Clients](http://git-scm.com/downloads/guis)
-1. [Pro Git book](http://git-scm.com/book)
+1. [GUI Clients](https://git-scm.com/downloads/guis)
+1. [Pro Git book](https://git-scm.com/book/en/v2)
1. [Platzi Course](https://courses.platzi.com/courses/git-gitlab/)
1. [Code School tutorial](http://try.github.io/)
1. Contact us at `subscribers@gitlab.com`
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 4b13e41ab53..1e424134242 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -13,7 +13,7 @@ NOTE: **Note:**
Support for MySQL was removed in GitLab 12.1. This procedure should be performed
**before** installing GitLab 12.1.
-[pgloader](http://pgloader.io) 3.4.1+ is required.
+[pgloader](https://pgloader.io/) 3.4.1+ is required.
You can install it directly from your distribution, for example in
Debian/Ubuntu:
@@ -59,7 +59,7 @@ pgloader within the container as it is not included in the container image.
```
1. Install pgloader:
-
+
``` bash
apt-get update
apt-get -y install pgloader
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 4300d6d56c7..0506d992d4b 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -100,7 +100,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
sudo -u git -H make
```
-### 8. Install/Update `gitlab-elasticsearch-indexer` (optional) **[STARTER ONLY]**
+### 8. Install/Update `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)**
If you're interested in using GitLab's new [elasticsearch repository indexer](../integration/elasticsearch.md#elasticsearch-repository-indexer-beta) (currently in beta)
please follow the instructions on the document linked above and enable the
diff --git a/doc/update/upgrading_from_ce_to_ee.md b/doc/update/upgrading_from_ce_to_ee.md
index 7ae716d2cb3..bea5bcd9dd7 100644
--- a/doc/update/upgrading_from_ce_to_ee.md
+++ b/doc/update/upgrading_from_ce_to_ee.md
@@ -72,7 +72,7 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
```
-### 4. Install `gitlab-elasticsearch-indexer` (optional) **[STARTER ONLY]**
+### 4. Install `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)**
If you're interested in using GitLab's new [elasticsearch repository
indexer](../integration/elasticsearch.md) (currently in beta) please follow the instructions on the
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 023dc7d6de3..d3b0a3c2829 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -76,7 +76,7 @@ sudo gem install bundler --no-document --version '< 2'
NOTE: Beginning in GitLab 11.8, we only support node 8 or higher, and dropped
support for node 6. Be sure to upgrade if necessary.
-GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+GitLab utilizes [webpack](https://webpack.js.org/) to compile frontend assets.
This requires a minimum version of node v8.10.0.
You can check which version you are running with `node -v`. If you are running
diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md
index e34ba045c54..427f3103cfc 100644
--- a/doc/user/admin_area/custom_project_templates.md
+++ b/doc/user/admin_area/custom_project_templates.md
@@ -1,6 +1,6 @@
-# Custom instance-level project templates **[PREMIUM ONLY]**
+# 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.
+> [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.
diff --git a/doc/user/admin_area/geo_nodes.md b/doc/user/admin_area/geo_nodes.md
index d99b87cbc5c..9e7057f93d4 100644
--- a/doc/user/admin_area/geo_nodes.md
+++ b/doc/user/admin_area/geo_nodes.md
@@ -2,7 +2,7 @@
type: howto
---
-# Geo nodes admin area **[PREMIUM ONLY]**
+# Geo nodes admin area **(PREMIUM ONLY)**
You can configure various settings for GitLab Geo nodes. For more information, see
[Geo documentation](../../administration/geo/replication/index.md).
@@ -61,6 +61,12 @@ which is used by users. Internal URL does not need to be a private address.
Internal URL defaults to External URL, but you can customize it under
**Admin area > Geo Nodes**.
+CAUTION: **Warning:**
+We recommend using an HTTPS connection while configuring the Geo nodes. To avoid
+breaking communication between **primary** and **secondary** nodes when using
+HTTPS, customize your Internal URL to point to a Load Balancer with TLS
+termination.
+
## Multiple secondary nodes behind a load balancer
In GitLab 11.11, **secondary** nodes can use identical external URLs as long as
@@ -83,4 +89,4 @@ 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. --> \ No newline at end of file
+but commented out to help encourage others to add to it in the future. -->
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index d2947ae3371..f5e6bff67c5 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -2,7 +2,7 @@
type: reference
---
-# GitLab Admin Area **[CORE ONLY]**
+# GitLab Admin Area **(CORE ONLY)**
The Admin Area provides a web UI for administering some features of GitLab self-managed instances.
@@ -26,9 +26,9 @@ The Admin Area is made up of the following sections:
| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
| Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
| Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
-| License **[STARTER ONLY]** | Upload, display, and remove [licenses](license.md). |
-| Push Rules **[STARTER]** | Configure pre-defined git [push rules](../../push_rules/push_rules.md) for projects. |
-| Geo **[PREMIUM ONLY]** | Configure and maintain [Geo nodes](geo_nodes.md). |
+| License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). |
+| Push Rules **(STARTER)** | Configure pre-defined git [push rules](../../push_rules/push_rules.md) for projects. |
+| Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). |
| Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
| Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
| Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
@@ -289,6 +289,6 @@ The content of each log file is listed in chronological order. To minimize perfo
The **Requests Profiles** page contains the token required for profiling. For more details, see [Request Profiling](../../administration/monitoring/performance/request_profiling.md).
-### Audit Log **[PREMIUM ONLY]**
+### Audit Log **(PREMIUM ONLY)**
The **Audit Log** page lists changes made within the GitLab server. With this information you can control, analyze, and track every change.
diff --git a/doc/user/admin_area/labels.md b/doc/user/admin_area/labels.md
index eba27548f86..1d15be89bd5 100644
--- a/doc/user/admin_area/labels.md
+++ b/doc/user/admin_area/labels.md
@@ -2,7 +2,7 @@
type: reference
---
-# Labels administration **[CORE ONLY]**
+# Labels administration **(CORE ONLY)**
In the Admin Area, you can manage labels for the GitLab instance. For more details, see [Labels](../project/labels.md).
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index 8ddb9c3d707..bbd04146eb2 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -2,7 +2,7 @@
type: howto
---
-# Activate all GitLab Enterprise Edition functionality with a license **[STARTER ONLY]**
+# Activate all GitLab Enterprise Edition functionality with a license **(STARTER ONLY)**
To activate all GitLab Enterprise Edition (EE) functionality, you need to upload
a license. Once you've received your license from GitLab Inc., you can upload it
diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 001e4b6bf48..9968b7349dc 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -4,7 +4,7 @@ type: reference
# Account and limit settings
-## Repository size limit **[STARTER]**
+## Repository size limit **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740) in [GitLab Enterprise Edition 8.12](https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee).
> Available in [GitLab Starter](https://about.gitlab.com/pricing/).
@@ -44,10 +44,12 @@ The first push of a new project, including LFS objects, will be checked for size
and **will** be rejected if the sum of their sizes exceeds the maximum allowed
repository size.
+**Note:** The repository size limit includes repository files and LFS, and does not include artifacts.
+
For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md).
NOTE: **Note:**
-For GitLab.com, the repository size limit is 10 GB.
+GitLab.com repository size [is set by GitLab](../../gitlab_com/index.md#repository-size-limit).
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 84596ff6a2c..ebbb2472752 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -2,14 +2,14 @@
type: reference
---
-# Continuous Integration and Deployment Admin settings **[CORE ONLY]**
+# Continuous Integration and Deployment Admin settings **(CORE ONLY)**
In this area, you will find settings for Auto DevOps, Runners and job artifacts.
You can find it in the admin area, under **Settings > Continuous Integration and Deployment**.
![Admin area settings button](../img/admin_area_settings_button.png)
-## Auto DevOps **[CORE ONLY]**
+## Auto DevOps **(CORE ONLY)**
To enable (or disable) [Auto DevOps](../../../topics/autodevops/index.md)
for all projects:
@@ -26,7 +26,7 @@ From now on, every existing project and newly created ones that don't have a
If you want to disable it for a specific project, you can do so in
[its settings](../../../topics/autodevops/index.md#enablingdisabling-auto-devops).
-## Maximum artifacts size **[CORE ONLY]**
+## Maximum artifacts size **(CORE ONLY)**
The maximum size of the [job artifacts](../../../administration/job_artifacts.md)
can be set in the Admin area of your GitLab instance. The value is in *MB* and
@@ -38,7 +38,7 @@ To change it:
1. Change the value of maximum artifacts size (in MB).
1. Hit **Save changes** for the changes to take effect.
-## Default artifacts expiration **[CORE ONLY]**
+## Default artifacts expiration **(CORE ONLY)**
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
can be set in the Admin area of your GitLab instance. The syntax of duration is
@@ -54,7 +54,7 @@ This setting is set per job and can be overridden in
[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifactsexpire_in).
To disable the expiration, set it to `0`. The default unit is in seconds.
-## Shared Runners pipeline minutes quota **[STARTER ONLY]**
+## Shared Runners pipeline minutes quota **(STARTER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1078)
in GitLab Starter 8.16.
@@ -137,7 +137,7 @@ includes a link to [purchase more minutes](https://customers.gitlab.com/plans).
If you are not the owner of the group, you will need to contact them to let them know they need to
[purchase more minutes](https://customers.gitlab.com/plans).
-## Archive jobs **[CORE ONLY]**
+## Archive jobs **(CORE ONLY)**
Archiving jobs is useful for reducing the CI/CD footprint on the system by
removing some of the capabilities of the jobs (metadata needed to run the job),
diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md
index 9555a695b13..1f07a4dfdc6 100644
--- a/doc/user/admin_area/settings/email.md
+++ b/doc/user/admin_area/settings/email.md
@@ -10,7 +10,7 @@ You can customize some of the content in emails sent from your GitLab instance.
The logo in the header of some emails can be customized, see the [logo customization section](../../../customization/branded_page_and_email_header.md).
-## Custom additional text **[PREMIUM ONLY]**
+## Custom additional text **(PREMIUM ONLY)**
> [Introduced][ee-5031] in [GitLab Premium][eep] 10.7.
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index c1aa04f7bc2..4fde7477490 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -2,10 +2,10 @@
type: reference
---
-# External authorization control **[CORE ONLY]**
+# External authorization control **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in
-> [GitLab Premium](https://about.gitlab.com/pricing) 10.6.
+> [GitLab Premium](https://about.gitlab.com/pricing/) 10.6.
> [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27056) to
> [GitLab Core](https://about.gitlab.com/pricing/) in 11.10.
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index eed087ae52b..5427d04cd7d 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -2,7 +2,7 @@
type: index
---
-# Admin Area settings **[CORE ONLY]**
+# Admin Area settings **(CORE ONLY)**
In the Admin Area **Settings** page, you can find various options for your GitLab
instance like sign-up restrictions, account limits and quota, metrics, etc.
@@ -10,7 +10,7 @@ instance like sign-up restrictions, account limits and quota, metrics, etc.
Navigate to it by going to **Admin Area > Settings**. Some of the settings
include:
-- [Account and limit settings](account_and_limit_settings.md) **[STARTER]**
+- [Account and limit settings](account_and_limit_settings.md) **(STARTER)**
- [Continuous Integration and Deployment](continuous_integration.md)
- [Email](email.md)
- [Sign up restrictions](sign_up_restrictions.md)
@@ -18,7 +18,7 @@ include:
- [Third party offers](third_party_offers.md)
- [Usage statistics](usage_statistics.md)
- [Visibility and access controls](visibility_and_access_controls.md)
-- [Custom templates repository](instance_template_repository.md) **[PREMIUM]**
+- [Custom templates repository](instance_template_repository.md) **(PREMIUM)**
NOTE: **Note:**
You can change the [first day of the week](../../profile/preferences.md) for the entire GitLab instance
diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md
index 91286a67c31..f2ba131d17b 100644
--- a/doc/user/admin_area/settings/instance_template_repository.md
+++ b/doc/user/admin_area/settings/instance_template_repository.md
@@ -2,10 +2,10 @@
type: reference
---
-# Instance template repository **[PREMIUM ONLY]**
+# Instance template repository **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5986) in
-> [GitLab Premium](https://about.gitlab.com/pricing) 11.3.
+> [GitLab Premium](https://about.gitlab.com/pricing/) 11.3.
## Overview
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index 652d6ad2cdd..f698e0a1608 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -10,7 +10,7 @@ to perform various actions.
All statistics are opt-out, you can enable/disable them from the admin panel
under **Admin area > Settings > Metrics and profiling > Usage statistics**.
-## Version check **[CORE ONLY]**
+## Version check **(CORE ONLY)**
If enabled, version check will inform you if a new version is available and the
importance of it through a status. This is shown on the help page (i.e. `/help`)
@@ -33,7 +33,7 @@ secure.
If you disable version check, this information will not be collected. Enable or
disable the version check at **Admin area > Settings > Usage statistics**.
-## Usage ping **[CORE ONLY]**
+## Usage ping **(CORE ONLY)**
> [Introduced][ee-557] in GitLab Enterprise Edition 8.10. More statistics
[were added][ee-735] in GitLab Enterprise Edition
@@ -78,7 +78,7 @@ production: &base
usage_ping_enabled: false
```
-## Instance statistics visibility **[CORE ONLY]**
+## Instance statistics visibility **(CORE ONLY)**
Once usage ping is enabled, GitLab will gather data from other instances and
will be able to show [usage statistics](../../instance_statistics/index.md)
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index 63879935fd8..bf59f49b993 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -12,7 +12,7 @@ GitLab allows administrators to:
- Enable or disable repository mirroring.
- Prevent non-administrators from deleting projects
([introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5615) in GitLab 12.0).
- **[PREMIUM ONLY]**
+ **(PREMIUM ONLY)**
To access the visibility and access control options:
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index 9dfbe326f1d..696446599c8 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -1,4 +1,4 @@
-# Container Scanning **[ULTIMATE]**
+# Container Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3672)
in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.4.
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index 2283efe3a44..936703cce32 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -1,4 +1,4 @@
-# Dynamic Application Security Testing (DAST) **[ULTIMATE]**
+# Dynamic Application Security Testing (DAST) **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4348)
in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.4.
diff --git a/doc/user/application_security/dependency_scanning/analyzers.md b/doc/user/application_security/dependency_scanning/analyzers.md
index 937ded287e5..3b4b341739b 100644
--- a/doc/user/application_security/dependency_scanning/analyzers.md
+++ b/doc/user/application_security/dependency_scanning/analyzers.md
@@ -1,4 +1,4 @@
-# Dependency Scanning Analyzers **[ULTIMATE]**
+# Dependency Scanning Analyzers **(ULTIMATE)**
Dependency Scanning relies on underlying third party tools that are wrapped into
what we call "Analyzers". An analyzer is a
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 9145e034dcb..2fe8a6f9029 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -1,4 +1,4 @@
-# Dependency Scanning **[ULTIMATE]**
+# Dependency Scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5105)
in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.7.
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 69fa1ec5da6..91e79f6c23b 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -1,4 +1,4 @@
-# GitLab Secure **[ULTIMATE]**
+# 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
@@ -12,12 +12,12 @@ GitLab can scan and report any vulnerabilities found in your project.
| Secure scanning tool | Description |
|:-----------------------------------------------------------------------------|:-----------------------------------------------------------------------|
-| [Container Scanning](container_scanning/index.md) **[ULTIMATE]** | Scan Docker containers for known vulnerabilities. |
-| [Dependency Scanning](dependency_scanning/index.md) **[ULTIMATE]** | Analyze your dependencies for known vulnerabilities. |
-| [Dynamic Application Security Testing (DAST)](dast/index.md) **[ULTIMATE]** | Analyze running web applications for known vulnerabilities. |
-| [License Management](license_management/index.md) **[ULTIMATE]** | Search your project's dependencies for their licenses. |
-| [Security Dashboard](security_dashboard/index.md) **[ULTIMATE]** | View vulnerabilities in all your projects and groups. |
-| [Static Application Security Testing (SAST)](sast/index.md) **[ULTIMATE]** | Analyze source code for known vulnerabilities. |
+| [Container Scanning](container_scanning/index.md) **(ULTIMATE)** | Scan Docker containers for known vulnerabilities. |
+| [Dependency Scanning](dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
+| [Dynamic Application Security Testing (DAST)](dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
+| [License Management](license_management/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
+| [Security Dashboard](security_dashboard/index.md) **(ULTIMATE)** | View vulnerabilities in all your projects and groups. |
+| [Static Application Security Testing (SAST)](sast/index.md) **(ULTIMATE)** | Analyze source code for known vulnerabilities. |
## Maintenance and update of the vulnerabilities database
diff --git a/doc/user/application_security/license_management/index.md b/doc/user/application_security/license_management/index.md
index 957c4ede981..8eb231f8359 100644
--- a/doc/user/application_security/license_management/index.md
+++ b/doc/user/application_security/license_management/index.md
@@ -1,4 +1,4 @@
-# License Management **[ULTIMATE]**
+# License Management **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5483)
in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0.
@@ -46,12 +46,19 @@ The following languages and package managers are supported.
| Language | Package managers | Scan Tool |
|------------|-------------------------------------------------------------------|----------------------------------------------------------|
-| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
-| Go | [Godep](https://github.com/tools/godep), go get |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| JavaScript | [Bower](https://bower.io/), [npm](https://www.npmjs.com/), [yarn](https://yarnpkg.com/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Go | [Godep](https://github.com/tools/godep), go get ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), gvt ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), glide ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), dep ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), trash ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) and govendor ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)), [go mod](https://github.com/golang/go/wiki/Modules) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Java | [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| .NET | [Nuget](https://www.nuget.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Python | [pip](https://pip.pypa.io/en/stable/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
| Ruby | [gem](https://rubygems.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Erlang | [rebar](https://www.rebar3.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) , [CocoaPods v0.39 and below](https://cocoapods.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Elixir | [mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types)) |[License Finder](https://github.com/pivotal/LicenseFinder)|
+| C++/C | [conan](https://conan.io/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Scala | [sbt](https://www.scala-sbt.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
+| Rust | [cargo](https://crates.io/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
+| PHP | [composer](https://getcomposer.org/) ([experimental support](https://github.com/pivotal/LicenseFinder#experimental-project-types))|[License Finder](https://github.com/pivotal/LicenseFinder)|
## Requirements
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 9074ac3f4a1..84b45cbe6e6 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -1,4 +1,4 @@
-# Static Application Security Testing (SAST) **[ULTIMATE]**
+# Static Application Security Testing (SAST) **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3775)
in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3.
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 19eeb06a259..3b01fe66e03 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -1,4 +1,4 @@
-# GitLab Security Dashboard **[ULTIMATE]**
+# GitLab Security Dashboard **(ULTIMATE)**
The Security Dashboard is a good place to get an overview of all the security
vulnerabilities in your groups and projects.
diff --git a/doc/user/asciidoc.md b/doc/user/asciidoc.md
index 0ed9bf3f518..06cc64b209d 100644
--- a/doc/user/asciidoc.md
+++ b/doc/user/asciidoc.md
@@ -1,7 +1,7 @@
# AsciiDoc
GitLab uses the [Asciidoctor](https://asciidoctor.org) gem to convert AsciiDoc content to HTML5.
-Consult the [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual) for a complete Asciidoctor reference.
+Consult the [Asciidoctor User Manual](https://asciidoctor.org/docs/user-manual/) for a complete Asciidoctor reference.
## Syntax
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 20eabdada79..f2156720af7 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -5,7 +5,7 @@ The ability to contribute conversationally is offered throughout GitLab.
You can leave a comment in the following places:
- issues
-- epics **[ULTIMATE]**
+- epics **(ULTIMATE)**
- merge requests
- snippets
- commits
@@ -282,7 +282,7 @@ edit existing comments. Non-team members are restricted from adding or editing c
Additionally, locked issues and merge requests can not be reopened.
-## Merge Request Reviews **[PREMIUM]**
+## Merge Request Reviews **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4213) in GitLab 11.4.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 886fb6e6f55..7858c419e04 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -1,7 +1,7 @@
# GitLab.com settings
In this page you will find information about the settings that are used on
-[GitLab.com](https://about.gitlab.com/pricing).
+[GitLab.com](https://about.gitlab.com/pricing/).
## SSH host keys fingerprints
@@ -73,9 +73,9 @@ or over the size limit, you can [reduce your repository size with Git](../projec
## IP range
-GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any
-IP based firewall can be configured by looking up all
-[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
+GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any
+IP based firewall can be configured by looking up all
+[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
[Static endpoints](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/5071) are being considered.
@@ -260,7 +260,7 @@ The list of GitLab.com specific settings (and their defaults) is as follows:
| hot_standby_feedback | on | off |
| log_autovacuum_min_duration | 0 | -1 |
| log_checkpoints | on | off |
-| log_line_prefix | `%t [%p]: [%l-1] ` | empty |
+| log_line_prefix | `%t [%p]: [%l-1]` | empty |
| log_min_duration_statement | 1000 | -1 |
| log_temp_files | 0 | -1 |
| maintenance_work_mem | 2048MB | 16 MB |
@@ -353,12 +353,10 @@ High Performance TCP/HTTP Load Balancer:
[4010]: https://gitlab.com/gitlab-com/infrastructure/issues/4010 "Find a good value for maximum timeout for Shared Runners"
[4070]: https://gitlab.com/gitlab-com/infrastructure/issues/4070 "Configure per-runner timeout for shared-runners-manager-X on GitLab.com"
-## Other admin area settings
+## Group and project settings
-This area highlights other noteworthy admin area settings on GitLab.com that differ from default settings. This list is not exhaustive.
+On GitLab.com, projects, groups, and snippets created
+after July 2019 have the `Internal` visibility setting disabled.
-NOTE: **Note:**
-From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
-and snippets on GitLab.com. Existing projects, groups, and snippets using the `Internal`
-visibility setting keep this setting. You can read more about the change in the
+You can read more about the change in the
[relevant issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12388).
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 8d4ffd93f59..0dffc216f8e 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -42,7 +42,7 @@ to the group containing the project if the project's cluster is available and no
In the case of sub-groups, GitLab will use the cluster of the closest ancestor group
to the project, provided the cluster is not disabled.
-## Multiple Kubernetes clusters **[PREMIUM]**
+## Multiple Kubernetes clusters **(PREMIUM)**
With GitLab Premium, you can associate more than one Kubernetes clusters to your
group. That way you can have different clusters for different environments,
@@ -82,7 +82,7 @@ the [Auto DevOps](../../../topics/autodevops/index.md) stages.
The domain should have a wildcard DNS configured to the Ingress IP address.
-## Environment scopes **[PREMIUM]**
+## Environment scopes **(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
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index a555b7723df..2d37fc375db 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -2,7 +2,7 @@
type: reference
---
-# Contribution Analytics **[STARTER]**
+# Contribution Analytics **(STARTER)**
> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md
index aa088d2fcdb..8a85c375490 100644
--- a/doc/user/group/custom_project_templates.md
+++ b/doc/user/group/custom_project_templates.md
@@ -2,9 +2,9 @@
type: reference
---
-# Custom group-level project templates **[PREMIUM]**
+# Custom group-level project templates **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing) 11.6.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.6.
When you create a new [project](../project/index.md), creating it based on custom project templates is
a convenient bootstrap option.
diff --git a/doc/user/group/dependency_proxy/index.md b/doc/user/group/dependency_proxy/index.md
index 4fc2d8e9509..771468fbba8 100644
--- a/doc/user/group/dependency_proxy/index.md
+++ b/doc/user/group/dependency_proxy/index.md
@@ -1,4 +1,4 @@
-# Dependency Proxy **[PREMIUM]**
+# Dependency Proxy **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7934) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.11.
diff --git a/doc/user/group/epics/index.md b/doc/user/group/epics/index.md
index f53c1dd95d7..601ffd4947b 100644
--- a/doc/user/group/epics/index.md
+++ b/doc/user/group/epics/index.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# Epics **[ULTIMATE]**
+# Epics **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 10.2.
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 7240b8e118b..db348c678eb 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -200,7 +200,7 @@ Alternatively, you can [lock the sharing with group feature](#share-with-group-l
In GitLab Enterprise Edition, it is possible to manage GitLab group memberships using LDAP groups.
See [the GitLab Enterprise Edition documentation](../../integration/ldap.md) for more information.
-## Epics **[ULTIMATE]**
+## Epics **(ULTIMATE)**
> Introduced in [GitLab Ultimate][ee] 10.2.
@@ -210,13 +210,13 @@ milestones.
[Learn more about Epics.](epics/index.md)
-## Group Security Dashboard **[ULTIMATE]**
+## Group Security Dashboard **(ULTIMATE)**
Get an overview of the vulnerabilities of all the projects in a group and its subgroups.
[Learn more about the Group Security Dashboard.](security_dashboard/index.md)
-## Insights **[ULTIMATE]**
+## Insights **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
@@ -307,7 +307,7 @@ To enable this feature, navigate to the group settings page. Select
![Checkbox for share with group lock](img/share_with_group_lock.png)
-#### Member Lock **[STARTER]**
+#### Member Lock **(STARTER)**
Member lock lets a group owner prevent any new project membership to all of the
projects within a group, allowing tighter control over project membership.
@@ -327,7 +327,7 @@ This will disable the option for all users who previously had permissions to
operate project memberships, so no new users can be added. Furthermore, any
request to add a new user to a project through API will not be possible.
-#### IP access restriction **[ULTIMATE]**
+#### IP access restriction **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1985) in
[GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
@@ -346,7 +346,7 @@ Restriction currently applies to UI, API access is not restricted.
To avoid accidental lock-out, admins and group owners are are able to access
the group regardless of the IP restriction.
-#### Group file templates **[PREMIUM]**
+#### Group file templates **(PREMIUM)**
Group file templates allow you to share a set of templates for common file
types with every project in a group. It is analogous to the
@@ -370,7 +370,7 @@ To enable this feature, navigate to the group settings page, expand the
![Group file template settings](img/group_file_template_settings.png)
-#### Group-level project templates **[PREMIUM]**
+#### Group-level project templates **(PREMIUM)**
Define project templates at a group level by setting a group as the template source.
[Learn more about group-level project templates](custom_project_templates.md).
@@ -382,10 +382,10 @@ Define project templates at a group level by setting a group as the template sou
- **Webhooks**: Configure [webhooks](../project/integrations/webhooks.md) for your group.
- **Kubernetes cluster integration**: Connect your GitLab group with [Kubernetes clusters](clusters/index.md).
- **Audit Events**: View [Audit Events](../../administration/audit_events.md)
- for the group. **[STARTER ONLY]**
+ for the group. **(STARTER ONLY)**
- **Pipelines quota**: Keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group.
-#### Storage usage quota **[STARTER]**
+#### Storage usage quota **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
@@ -393,17 +393,17 @@ A group owner can check the aggregated storage usage for all the project in a gr
![Group storage usage quota](img/group_storage_usage_quota.png)
-## User contribution analysis **[STARTER]**
+## User contribution analysis **(STARTER)**
With [GitLab Contribution Analytics](contribution_analytics/index.md),
you have an overview of the contributions (pushes, merge requests,
and issues) performed by your group members.
-## Issues analytics **[PREMIUM]**
+## Issues analytics **(PREMIUM)**
With [GitLab Issues Analytics](issues_analytics/index.md), you can see a bar chart of the number of issues created each month in your groups.
-## Dependency Proxy **[PREMIUM]**
+## Dependency Proxy **(PREMIUM)**
Use GitLab as a [dependency proxy](dependency_proxy/index.md) for upstream Docker images.
diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md
index e6ba47939b3..f0e7f7239c1 100644
--- a/doc/user/group/insights/index.md
+++ b/doc/user/group/insights/index.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# Insights **[ULTIMATE]**
+# Insights **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
diff --git a/doc/user/group/issues_analytics/index.md b/doc/user/group/issues_analytics/index.md
index 46d5c1e2e09..dc4b057789f 100644
--- a/doc/user/group/issues_analytics/index.md
+++ b/doc/user/group/issues_analytics/index.md
@@ -2,7 +2,7 @@
type: reference
---
-# Issues Analytics **[PREMIUM]**
+# Issues Analytics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7478) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.5.
diff --git a/doc/user/group/roadmap/index.md b/doc/user/group/roadmap/index.md
index 683c715c8d5..a72cd990706 100644
--- a/doc/user/group/roadmap/index.md
+++ b/doc/user/group/roadmap/index.md
@@ -2,9 +2,9 @@
type: reference
---
-# Roadmap **[ULTIMATE]**
+# Roadmap **(ULTIMATE)**
-> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing) 10.5.
+> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.5.
An Epic within a group containing **Start date** and/or **Due date**
can be visualized in a form of a timeline (e.g. a Gantt chart). The Epics Roadmap page
@@ -30,7 +30,7 @@ Roadmaps can also be [visualized inside an epic](../epics/index.md#roadmap-in-ep
## Timeline duration
-> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing) 11.0.
+> Introduced in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0.
Roadmap supports the following date ranges:
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 26893f7e31e..54923ab69ff 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# SAML SSO for GitLab.com Groups **[SILVER ONLY]**
+# SAML SSO for GitLab.com Groups **(SILVER ONLY)**
> Introduced in [GitLab.com Silver](https://about.gitlab.com/pricing/) 11.0.
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 5aef463d782..2d408766db8 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -2,7 +2,7 @@
type: howto, reference
---
-# SCIM provisioning using SAML SSO for Groups **[SILVER ONLY]**
+# SCIM provisioning using SAML SSO for Groups **(SILVER ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9388) in [GitLab.com Silver](https://about.gitlab.com/pricing/) 11.10.
diff --git a/doc/user/index.md b/doc/user/index.md
index 899026a801f..501d74c76d1 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -173,7 +173,7 @@ Learn what is [Git](../topics/git/index.md) and its best practices.
See [various statistics](instance_statistics/index.md) of your GitLab instance.
-## Operations Dashboard **[PREMIUM]**
+## Operations Dashboard **(PREMIUM)**
See [Operations Dashboard](operations_dashboard/index.md) for a summary of each
project's operational health.
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index eaae9964367..83629fc2e4b 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -25,7 +25,7 @@ You can use GFM in the following areas:
- Snippets (the snippet must be named with a `.md` extension)
- Wiki pages
- Markdown documents inside repositories
-- Epics **[ULTIMATE]**
+- Epics **(ULTIMATE)**
You can also use other rich text files in GitLab. You might have to install a dependency
to do so. Please see the [`gitlab-markup` gem project](https://gitlab.com/gitlab-org/gitlab-markup)
@@ -368,7 +368,7 @@ GFM will recognize the following:
| issue | ``#123`` | `namespace/project#123` | `project#123` |
| merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` |
-| epic **[ULTIMATE]** | `&123` | `group1/subgroup&123` | |
+| epic **(ULTIMATE)** | `&123` | `group1/subgroup&123` | |
| label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md
index 54bf3ff8a40..8c4d387190a 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -1,4 +1,4 @@
-# Operations Dashboard **[PREMIUM]**
+# Operations Dashboard **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5781) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.5. [Moved](https://gitlab.com/gitlab-org/gitlab-ee/issues/9218) to [GitLab Premium](https://about.gitlab.com/pricing/) in 11.10.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 33fff0fed74..1b279173d1c 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -43,10 +43,10 @@ The following table depicts the various user permission levels in a project.
|---------------------------------------------------|---------|------------|-------------|----------|--------|
| Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
-| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View approved/blacklisted licenses **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View license management reports **[ULTIMATE]** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
-| View Security reports **[ULTIMATE]** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
+| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View approved/blacklisted licenses **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View license management reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
+| View Security reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -62,8 +62,8 @@ The following table depicts the various user permission levels in a project.
| Label issues | | ✓ | ✓ | ✓ | ✓ |
| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
-| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ |
-| Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| Manage related issues **(STARTER)** | | ✓ | ✓ | ✓ | ✓ |
+| Create issue from vulnerability **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
@@ -72,8 +72,8 @@ The following table depicts the various user permission levels in a project.
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
| View project statistics | | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
-| Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **[PREMIUM]** | | ✓ | ✓ | ✓ | ✓ |
-| Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **[PREMIUM]** | | | ✓ | ✓ | ✓ ||
+| Pull from [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
+| Publish to [Maven repository](project/packages/maven_repository.md) or [NPM registry](project/packages/npm_registry.md) **(PREMIUM)** | | | ✓ | ✓ | ✓ ||
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
@@ -91,13 +91,13 @@ The following table depicts the various user permission levels in a project.
| Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
-| Use security dashboard **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
-| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| Dismiss vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Apply code change suggestions | | | ✓ | ✓ | ✓ |
| Create and edit wiki pages | | | ✓ | ✓ | ✓ |
| Rewrite/remove Git tags | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
-| Run Web IDE's Interactive Web Terminals **[ULTIMATE ONLY]** | | | | ✓ | ✓ |
+| Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Enable/disable branch protection | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
@@ -113,7 +113,7 @@ The following table depicts the various user permission levels in a project.
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
| Remove GitLab Pages | | | | ✓ | ✓ |
| Manage clusters | | | | ✓ | ✓ |
-| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
+| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Manage Error Tracking | | | | ✓ | ✓ |
| Delete wiki pages | | | | ✓ | ✓ |
@@ -167,7 +167,7 @@ and drag issues around. Read though the
[documentation on Issue Boards permissions](project/issue_board.md#permissions)
to learn more.
-### File Locking permissions **[PREMIUM]**
+### File Locking permissions **(PREMIUM)**
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
@@ -202,18 +202,18 @@ group.
| Action | Guest | Reporter | Developer | Maintainer | Owner |
|-------------------------------------------------|-------|----------|-----------|------------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View Insights charts **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| View Insights charts **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View group epic **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit group epic **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
| Create project in group | | | ✓ | ✓ | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
-| Enable/disable a dependency proxy **[PREMIUM]** | | | ✓ | ✓ | ✓ |
+| Enable/disable a dependency proxy **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Edit group | | | | | ✓ |
| Create subgroup | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
-| Delete group epic **[ULTIMATE]** | | | | | ✓ |
+| Delete group epic **(ULTIMATE)** | | | | | ✓ |
| View group Audit Events | | | | | ✓ |
### Subgroup permissions
@@ -264,7 +264,7 @@ Here are some examples:
Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia.
-## Auditor users **[PREMIUM ONLY]**
+## Auditor users **(PREMIUM ONLY)**
>[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.17.
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index b61216b7b67..b1fde3b577b 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -79,7 +79,7 @@ You have 8 options here that you can use for your default dashboard view:
- Your [Todos](../../workflow/todos.md)
- Assigned Issues
- Assigned Merge Requests
-- Operations Dashboard **[PREMIUM]**
+- Operations Dashboard **(PREMIUM)**
### Project overview content
diff --git a/doc/user/project/canary_deployments.md b/doc/user/project/canary_deployments.md
index 9bb282f1b78..5068d2757be 100644
--- a/doc/user/project/canary_deployments.md
+++ b/doc/user/project/canary_deployments.md
@@ -1,4 +1,4 @@
-# Canary Deployments **[PREMIUM]**
+# Canary Deployments **(PREMIUM)**
> [Introduced][ee-1659] in [GitLab Premium][eep] 9.1.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index d21455fb5ca..56f8257fbe7 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -5,6 +5,9 @@
Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes
cluster in a few steps.
+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
@@ -432,7 +435,7 @@ record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) such as `*.example.co
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]**
+## Multiple Kubernetes clusters **(PREMIUM)**
> Introduced in [GitLab Premium][ee] 10.3.
@@ -444,7 +447,7 @@ 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]**
+## 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
@@ -547,7 +550,7 @@ in a way that causes this error. Ensure you deselect the
[GitLab-managed cluster](#gitlab-managed-clusters) option if you want to manage
namespaces and service accounts yourself.
-## Monitoring your Kubernetes cluster **[ULTIMATE]**
+## Monitoring your Kubernetes cluster **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4701) in [GitLab Ultimate][ee] 10.6.
@@ -585,7 +588,7 @@ and add a Kubernetes cluster again.
Here's what you can do with GitLab if you enable the Kubernetes integration.
-### Deploy Boards **[PREMIUM]**
+### 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,
@@ -595,7 +598,7 @@ workflow they already use without any need to access Kubernetes.
[Read more about Deploy Boards](../deploy_boards.md)
-### Canary Deployments **[PREMIUM]**
+### 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
@@ -603,7 +606,7 @@ the need to leave GitLab.
[Read more about Canary Deployments](../canary_deployments.md)
-### Pod logs **[ULTIMATE]**
+### 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.
diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md
index 25d8abebf07..864cd75823c 100644
--- a/doc/user/project/clusters/kubernetes_pod_logs.md
+++ b/doc/user/project/clusters/kubernetes_pod_logs.md
@@ -1,4 +1,4 @@
-# Kubernetes Pod Logs **[ULTIMATE]**
+# Kubernetes Pod Logs **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4752) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0.
diff --git a/doc/user/project/code_owners.md b/doc/user/project/code_owners.md
index ae04616943f..78ffa11d59b 100644
--- a/doc/user/project/code_owners.md
+++ b/doc/user/project/code_owners.md
@@ -1,10 +1,12 @@
-# Code Owners **[STARTER]**
+# Code Owners **(STARTER)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6916)
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6916)
in [GitLab Starter](https://about.gitlab.com/pricing/) 11.3.
+> - [Support for group namespaces](https://gitlab.com/gitlab-org/gitlab-ce/issues/53182) added in GitLab Starter 12.1.
-You can use a `CODEOWNERS` file to specify users that are responsible
-for certain files in a repository.
+You can use a `CODEOWNERS` file to specify users or
+[shared groups](members/share_project_with_groups.md)
+that are responsible for certain files in a repository.
You can choose and add the `CODEOWNERS` file in three places:
@@ -25,7 +27,8 @@ the given file.
Files can be specified using the same kind of patterns you would use
in the `.gitignore` file followed by the `@username` or email of one
-or more users that should be owners of the file.
+or more users or by the `@name` of one or more groups that should
+be owners of the file.
The order in which the paths are defined is significant: the last
pattern that matches a given path will be used to find the code
@@ -63,6 +66,10 @@ CODEOWNERS @multiple @owners @tab-separated
# owner for the LICENSE file
LICENSE @legal this_does_not_match janedoe@gitlab.com
+# Group names can be used to match groups and nested groups to specify
+# them as owners for a file
+README @group @group/with-nested/subgroup
+
# Ending a path in a `/` will specify the code owners for every file
# nested in that directory, on any level
/docs/ @all-docs
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 7d567da1c9a..eac7fc6b195 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -29,7 +29,7 @@ to enable it.
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.html#repository-size).
+ 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)
and enable the **Container Registry** feature on your project. For new
projects this might be enabled by default. For existing projects
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index dc97a44fd68..5d36e1d4be3 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -156,6 +156,6 @@ Learn more about Cycle Analytics in the following resources:
[environment]: ../../ci/yaml/README.md#environment
[GitLab flow]: ../../workflow/gitlab_flow.md
[idea to production]: https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab
-[issue closing pattern]: issues/automatic_issue_closing.md
+[issue closing pattern]: issues/managing_issues.md#closing-issues-automatically
[permissions]: ../permissions.md
[yml]: ../../ci/yaml/README.md
diff --git a/doc/user/project/deploy_boards.md b/doc/user/project/deploy_boards.md
index 175384bc985..cb1faa771bc 100644
--- a/doc/user/project/deploy_boards.md
+++ b/doc/user/project/deploy_boards.md
@@ -1,4 +1,4 @@
-# Deploy Boards **[PREMIUM]**
+# Deploy Boards **(PREMIUM)**
> [Introduced][ee-1589] in [GitLab Premium][ee] 9.0.
@@ -125,6 +125,6 @@ version of your application.
[kube-service]: integrations/kubernetes.md "Kubernetes project service"
[review apps]: ../../ci/review_apps/index.md "Review Apps documentation"
[variables]: ../../ci/variables/README.md "GitLab CI variables"
-[autodeploy]: ../../ci/autodeploy/index.md "GitLab Autodeploy"
+[autodeploy]: ../../topics/autodevops/index.md#auto-deploy "GitLab Autodeploy"
[kube-image]: https://gitlab.com/gitlab-examples/kubernetes-deploy/container_registry "Kubernetes deploy Container Registry"
[runners]: ../../ci/runners/README.md
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 5e11e7c0203..72594733cd3 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -41,7 +41,7 @@ the following table.
## Deploy token custom username
-> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29639] in GitLab 12.1.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/29639) in GitLab 12.1.
The default username format is `gitlab+deploy-token-#{n}`. Some tools or platforms may not support this format,
in such case you can specify custom username to be used when creating the deploy token.
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 7520237251a..196874fdc86 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -55,7 +55,7 @@ changes you made after picking the template and return it to its initial status.
![Description templates](img/description_templates.png)
-## Setting a default template for issues and merge requests **[STARTER]**
+## Setting a default template for issues and merge requests **(STARTER)**
> **Notes:**
>
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index 40603790c12..dec679fc975 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -1,4 +1,4 @@
-# File Locking **[PREMIUM]**
+# File Locking **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/440) in [GitLab Premium](https://about.gitlab.com/pricing/) 8.9.
diff --git a/doc/user/project/import/gemnasium.md b/doc/user/project/import/gemnasium.md
index 3b071ff590f..0afa32e4133 100644
--- a/doc/user/project/import/gemnasium.md
+++ b/doc/user/project/import/gemnasium.md
@@ -1,4 +1,4 @@
-# Gemnasium **[ULTIMATE]**
+# Gemnasium **(ULTIMATE)**
This guide describes how to migrate from Gemnasium.com to your own GitLab
instance or GitLab.com.
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index e194d57e2e0..cdb7f837158 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -121,10 +121,10 @@ Depending your GitLab tier, [project mirroring](../../../workflow/repository_mir
your imported project in sync with its GitHub copy.
Additionally, you can configure GitLab to send pipeline status updates back GitHub with the
-[GitHub Project Integration](../integrations/github.md). **[PREMIUM]**
+[GitHub Project Integration](../integrations/github.md). **(PREMIUM)**
If you import your project using [CI/CD for external repo](../../../ci/ci_cd_for_external_repos/index.md), then both
-of the above are automatically configured. **[PREMIUM]**
+of the above are automatically configured. **(PREMIUM)**
## Improving the speed of imports on self-hosted instances
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 2b6927bd780..334be713aa5 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -20,7 +20,7 @@ In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
repository is too large the import can timeout.
-There is also the option of [connecting your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md). **[PREMIUM]**
+There is also the option of [connecting your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)**
## Migrating from self-hosted GitLab to GitLab.com
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index 4825b005a85..7359487e1bf 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -29,7 +29,7 @@ directly in a filesystem level.
1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
-1. Download SubGit from <https://subgit.com/download/>.
+1. Download SubGit from <https://subgit.com/download>.
1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
command will be available at `/opt/subgit-VERSION/bin/subgit`.
diff --git a/doc/user/project/import/tfs.md b/doc/user/project/import/tfs.md
index 8727c2ff6c3..b4597a4da60 100644
--- a/doc/user/project/import/tfs.md
+++ b/doc/user/project/import/tfs.md
@@ -1,6 +1,6 @@
# Migrating from TFS
-[TFS](https://www.visualstudio.com/tfs/) is a set of tools developed by Microsoft
+[TFS](https://visualstudio.microsoft.com/tfs/) is a set of tools developed by Microsoft
which also includes a centralized version control system (TFVC) similar to Git.
In this document, we emphasize on the TFVC to Git migration.
@@ -18,10 +18,10 @@ The following list illustrates the main differences between TFVC and Git:
a committed file(s) is stored in its entirety (snapshot). That means that's
very easy in Git to revert or undo a whole change.
-_Check also Microsoft's documentation on the
-[comparison of Git and TFVC](https://www.visualstudio.com/en-us/docs/tfvc/comparison-git-tfvc)
-and the Wikipedia article on
-[comparing the different version control software](https://en.wikipedia.org/wiki/Comparison_of_version_control_software)._
+Check also Microsoft's documentation on the
+[comparison of Git and TFVC](https://docs.microsoft.com/en-us/azure/devops/repos/tfvc/comparison-git-tfvc?view=azure-devops)
+and the Wikipedia
+[comparison of version control software](https://en.wikipedia.org/wiki/Comparison_of_version_control_software).
## Why migrate
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 06286951e20..f332281fa82 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -17,7 +17,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Issue tracker](issues/index.md): Discuss implementations with your team within issues
- [Issue Boards](issue_board.md): Organize and prioritize your workflow
- - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]**
+ - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **(STARTER)**
- [Repositories](repository/index.md): Host your code in a fully
integrated platform
- [Branches](repository/branches/index.md): use Git branching strategies to
@@ -34,11 +34,11 @@ When you create a project in GitLab, you'll have access to a large number of
- [Issue tracker](issues/index.md): Discuss implementations with your team within issues
- [Issue Boards](issue_board.md): Organize and prioritize your workflow
- - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]**
+ - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **(STARTER)**
- [Merge Requests](merge_requests/index.md): Apply your branching
strategy and get reviewed by your team
- [Merge Request Approvals](merge_requests/merge_request_approvals.md): Ask for approval before
- implementing a change **[STARTER]**
+ implementing a change **(STARTER)**
- [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
Your Git diff tool right from GitLab's UI
- [Review Apps](../../ci/review_apps/index.md): Live preview the results
@@ -75,7 +75,7 @@ When you create a project in GitLab, you'll have access to a large number of
- [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project
with a Kubernetes cluster
- [Feature Flags](operations/feature_flags.md): Feature flags allow you to ship a project in
- different flavors by dynamically toggling certain functionality **[PREMIUM]**
+ different flavors by dynamically toggling certain functionality **(PREMIUM)**
- [GitLab Pages](pages/index.md): Build, test, and deploy your static
website with GitLab Pages
@@ -84,18 +84,18 @@ When you create a project in GitLab, you'll have access to a large number of
- [Wiki](wiki/index.md): document your GitLab project in an integrated Wiki.
- [Snippets](../snippets.md): store, share and collaborate on code snippets.
- [Cycle Analytics](cycle_analytics.md): review your development lifecycle.
-- [Insights](insights/index.md): configure the Insights that matter for your projects. **[ULTIMATE]**
-- [Security Dashboard](security_dashboard.md): Security Dashboard. **[ULTIMATE]**
+- [Insights](insights/index.md): configure the Insights that matter for your projects. **(ULTIMATE)**
+- [Security Dashboard](security_dashboard.md): Security Dashboard. **(ULTIMATE)**
- [Syntax highlighting](highlighting.md): an alternative to customize
your code blocks, overriding GitLab's default choice of language.
- [Badges](badges.md): badges for the project overview.
- [Releases](releases/index.md): a way to track deliverables in your project as snapshot in time of
the source, build output, and other metadata or artifacts
associated with a released version of your code.
-- [Maven packages](packages/maven_repository.md): your private Maven repository in GitLab. **[PREMIUM]**
-- [NPM packages](packages/npm_registry.md): your private NPM package registry in GitLab. **[PREMIUM]**
-- [Code owners](code_owners.md): specify code owners for certain files **[STARTER]**
-- [License Management](../application_security/license_management/index.md): approve and blacklist licenses for projects. **[ULTIMATE]**
+- [Maven packages](packages/maven_repository.md): your private Maven repository in GitLab. **(PREMIUM)**
+- [NPM packages](packages/npm_registry.md): your private NPM package registry in GitLab. **(PREMIUM)**
+- [Code owners](code_owners.md): specify code owners for certain files **(STARTER)**
+- [License Management](../application_security/license_management/index.md): approve and blacklist licenses for projects. **(ULTIMATE)**
### Project integrations
@@ -131,7 +131,7 @@ Read through the documentation on [project settings](settings/index.md).
- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
- [Importing and exporting projects between GitLab instances](settings/import_export.md)
-## CI/CD for external repositories **[PREMIUM]**
+## CI/CD for external repositories **(PREMIUM)**
Instead of importing a repository directly to GitLab, you can connect your repository
as a CI/CD project.
@@ -193,7 +193,7 @@ password <personal_access_token>
To quickly access a project from the GitLab UI using the project ID,
visit the `/projects/:id` URL in your browser or other tool accessing the project.
-## Project aliases **[PREMIUM ONLY]**
+## Project aliases **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1.
diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md
index 1c6ad0b8b2b..76a6a96eec5 100644
--- a/doc/user/project/insights/index.md
+++ b/doc/user/project/insights/index.md
@@ -1,4 +1,4 @@
-# Insights **[ULTIMATE]**
+# Insights **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.0.
diff --git a/doc/user/project/integrations/github.md b/doc/user/project/integrations/github.md
index 680fcdb78bb..d0399f9193b 100644
--- a/doc/user/project/integrations/github.md
+++ b/doc/user/project/integrations/github.md
@@ -1,4 +1,4 @@
-# GitHub project integration **[PREMIUM]**
+# GitHub project integration **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3836) in GitLab Premium 10.6.
@@ -14,7 +14,7 @@ and is automatically configured on [GitHub import](../../../integration/github.m
### Complete these steps on GitHub
-This integration requires a [GitHub API token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)
+This integration requires a [GitHub API token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line)
with `repo:status` access granted:
1. Go to your "Personal access tokens" page at <https://github.com/settings/tokens>
diff --git a/doc/user/project/integrations/img/jira_api_token.png b/doc/user/project/integrations/img/jira_api_token.png
index 4fa7a46854e..29689271bf7 100644
--- a/doc/user/project/integrations/img/jira_api_token.png
+++ b/doc/user/project/integrations/img/jira_api_token.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_api_token_menu.png b/doc/user/project/integrations/img/jira_api_token_menu.png
index 14037bd0b47..1aca1d78f36 100644
--- a/doc/user/project/integrations/img/jira_api_token_menu.png
+++ b/doc/user/project/integrations/img/jira_api_token_menu.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_issue_reference.png b/doc/user/project/integrations/img/jira_issue_reference.png
index 72c81460df7..a3e80c1b054 100644
--- a/doc/user/project/integrations/img/jira_issue_reference.png
+++ b/doc/user/project/integrations/img/jira_issue_reference.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_merge_request_close.png b/doc/user/project/integrations/img/jira_merge_request_close.png
index 0f82ceba557..1c089c94207 100644
--- a/doc/user/project/integrations/img/jira_merge_request_close.png
+++ b/doc/user/project/integrations/img/jira_merge_request_close.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_close_comment.png b/doc/user/project/integrations/img/jira_service_close_comment.png
deleted file mode 100644
index 9af0d38f098..00000000000
--- a/doc/user/project/integrations/img/jira_service_close_comment.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_close_issue.png b/doc/user/project/integrations/img/jira_service_close_issue.png
index c85b1d1dd97..73d6498192c 100644
--- a/doc/user/project/integrations/img/jira_service_close_issue.png
+++ b/doc/user/project/integrations/img/jira_service_close_issue.png
Binary files differ
diff --git a/doc/user/project/integrations/img/jira_service_page.png b/doc/user/project/integrations/img/jira_service_page.png
index 377b69d9d06..80dd65ea24e 100644
--- a/doc/user/project/integrations/img/jira_service_page.png
+++ b/doc/user/project/integrations/img/jira_service_page.png
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 8f2e5a55b5f..ca990ee6c32 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -39,21 +39,17 @@ a GitLab project with any single Jira project.
If you have one Jira instance, you can pre-fill the settings page with a default
template. See the [Services Templates][services-templates] docs.
-Configuration happens via user name and password. Connecting to a Jira Server
-via CAS is not possible.
-
-In order to enable the Jira service in GitLab, you need to first configure the
-project in Jira and then enter the correct values in GitLab.
+In order to enable the Jira service in GitLab, you need to first configure the project in Jira and then enter the correct values in GitLab.
### Configuring Jira
-When connecting to **Jira Server**, which supports basic authentication, a **username and password** are required. Check the link below and proceed to the next step:
+#### Jira Server
-- [Setting up a user in Jira Server](jira_server_configuration.md)
+When connecting to **Jira Server**, which supports basic authentication, a **username and password** are required. Note that connecting to a Jira server via CAS is not possible. [Set up a user in Jira Server](jira_server_configuration.md) first and then proceed to [Configuring GitLab](#configuring-gitlab).
-When connecting to **Jira Cloud**, which supports authentication via API token, an **email and API token**, are required. Check the link below and proceed to the next step:
+#### Jira Cloud
-- [Setting up a user in Jira Cloud](jira_cloud_configuration.md)
+When connecting to **Jira Cloud**, which supports authentication via API token, an **email and API token**, are required. [Set up a user in Jira Cloud](jira_cloud_configuration.md) first and then proceed to [Configuring GitLab](#configuring-gitlab).
### Configuring GitLab
@@ -68,7 +64,7 @@ When connecting to **Jira Cloud**, which supports authentication via API token,
> to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with
> a value of `fromDialog`.
-To enable Jira integration in a project, navigate to the
+To enable the Jira integration in a project, navigate to the
[Integrations page](project_services.md#accessing-the-project-services), click
the **Jira** service, and fill in the required details on the page as described
in the table below.
@@ -127,6 +123,12 @@ ENTITY_TITLE
![example of mentioning or closing the Jira issue](img/jira_issue_reference.png)
+For example, the following commit will reference the Jira issue with `PROJECT-1` as its ID:
+
+```bash
+git commit -m "PROJECT-1 Fix spelling and grammar"
+```
+
### Closing Jira Issues
Jira issues can be closed directly from GitLab by using trigger words in
@@ -142,7 +144,7 @@ the same goal:
- `Closes PROJECT-1`
- `Fixes PROJECT-1`
-where `PROJECT-1` is the issue ID of the Jira project.
+where `PROJECT-1` is the ID of the Jira issue.
> **Notes:**
>
@@ -174,8 +176,6 @@ with a link to the commit that resolved the issue.
![The GitLab integration closes Jira issue](img/jira_service_close_issue.png)
-![The GitLab integration creates a comment and a link on Jira issue.](img/jira_service_close_comment.png)
-
## Troubleshooting
If these features do not work as expected, it is likely due to a problem with the way the integration settings were configured.
diff --git a/doc/user/project/integrations/jira_cloud_configuration.md b/doc/user/project/integrations/jira_cloud_configuration.md
index 614f05d5b7e..5a5ba2dd168 100644
--- a/doc/user/project/integrations/jira_cloud_configuration.md
+++ b/doc/user/project/integrations/jira_cloud_configuration.md
@@ -3,16 +3,18 @@
An API token is needed when integrating with Jira Cloud, follow the steps
below to create one:
-1. Log in to <https://id.atlassian.com> with your email.
-1. **Click API tokens**, then **Create API token**.
+1. Log in to <https://id.atlassian.com/manage/api-tokens> with your email address.
+
+ NOTE: **Note**
+ It is important that the user associated with this email address has *write* access
+ to projects in Jira.
+
+2. Click **Create API token**.
![Jira API token](img/jira_api_token_menu.png)
![Jira API token](img/jira_api_token.png)
-1. Make sure to write down your new API token as you will need it in the next [steps](jira.md#configuring-gitlab).
-
-NOTE: **Note**
-It is important that the user associated with this email has 'write' access to projects in Jira.
+1. Click **Copy to clipboard**, or click **View** and write down the new API token. It is required when [configuring GitLab](jira.md#configuring-gitlab).
-The Jira configuration is complete. You are going to need this newly created token and the email you used to log in, when [configuring GitLab in the next section](jira.md#configuring-gitlab).
+The Jira configuration is complete. You need the newly created token, and the associated email address, when [configuring GitLab](jira.md#configuring-gitlab) in the next section.
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 0e4c71a9d3e..62e08a183f7 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -34,12 +34,12 @@ Click on the service links to see further configuration instructions and details
| [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients |
| External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
| Flowdock | Flowdock is a collaboration web app for technical teams |
-| [GitHub](github.md) **[PREMIUM]** | Sends pipeline notifications to GitHub |
+| [GitHub](github.md) **(PREMIUM)** | Sends pipeline notifications to GitHub |
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
| [HipChat](hipchat.md) | Private group chat and IM |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
| [Jira](jira.md) | Jira issue tracker |
-| [Jenkins](../../../integration/jenkins.md) **[STARTER]** | An extendable open source continuous integration server |
+| [Jenkins](../../../integration/jenkins.md) **(STARTER)** | An extendable open source continuous integration server |
| JetBrains TeamCity CI | A continuous integration and build server |
| [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands |
| [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost |
@@ -47,7 +47,7 @@ Click on the service links to see further configuration instructions and details
| Packagist | Update your project on Packagist, the main Composer repository |
| Pipelines emails | Email the pipeline status to a list of recipients |
| [Slack Notifications](slack.md) | Send GitLab events (e.g. issue created) to Slack as notifications |
-| [Slack slash commands](slack_slash_commands.md) **[CORE ONLY]** | Use slash commands in Slack to control GitLab |
+| [Slack slash commands](slack_slash_commands.md) **(CORE ONLY)** | Use slash commands in Slack to control GitLab |
| [GitLab Slack application](gitlab_slack_application.md) **[FREE ONLY]** | Use Slack's official application |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| [Prometheus](prometheus.md) | Monitor the performance of your deployed apps |
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index aab7131e353..01b6650bfab 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -18,6 +18,7 @@ Once enabled, GitLab will automatically detect metrics from known services in th
## Enabling Prometheus Integration
### Managed Prometheus on Kubernetes
+
> **Note**: [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28916) in GitLab 10.5
GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cluster](../clusters/index.md), making monitoring of your apps easy.
@@ -39,9 +40,9 @@ Once you have a connected Kubernetes cluster with Helm installed, deploying a ma
#### About managed Prometheus deployments
-Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
+Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
-The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Ckubernetes_sd_config%3E) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):
+The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):
- `prometheus.io/scrape` to `true` to enable monitoring of the resource.
- `prometheus.io/port` to define the port of the metrics endpoint.
@@ -66,9 +67,9 @@ Integration with Prometheus requires the following:
Installing and configuring Prometheus to monitor applications is fairly straight forward.
-1. [Install Prometheus](https://prometheus.io/docs/introduction/install/)
+1. [Install Prometheus](https://prometheus.io/docs/prometheus/latest/installation/)
1. Set up one of the [supported monitoring targets](prometheus_library/index.md)
-1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config)
+1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config)
#### Configuration in GitLab
@@ -93,7 +94,7 @@ GitLab will automatically scan the Prometheus server for metrics from known serv
You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments).
-### Adding additional metrics **[PREMIUM]**
+### Adding additional metrics **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3799) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.6.
@@ -120,7 +121,7 @@ GitLab supports a limited set of [CI variables](../../../ci/variables/README.htm
To specify a variable in a query, enclose it in curly braces with a leading percent. For example: `%{ci_environment_slug}`.
-### Setting up alerts for Prometheus metrics **[ULTIMATE]**
+### Setting up alerts for Prometheus metrics **(ULTIMATE)**
#### Managed Prometheus instances
@@ -156,7 +157,7 @@ receivers:
...
```
-### Taking action on incidents **[ULTIMATE]**
+### Taking action on incidents **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4925) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11.
diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md
index 66f1b587070..5049733abdd 100644
--- a/doc/user/project/integrations/prometheus_library/cloudwatch.md
+++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md
@@ -18,9 +18,9 @@ The [Prometheus service](../prometheus.md) must be enabled.
## Configuring Prometheus to monitor for Cloudwatch metrics
-To get started with Cloudwatch monitoring, you should install and configure the [Cloudwatch exporter](https://github.com/hnlq715/nginx-vts-exporter) which retrieves and parses the specified Cloudwatch metrics and translates them into a Prometheus monitoring endpoint.
+To get started with Cloudwatch monitoring, you should install and configure the [Cloudwatch exporter](https://github.com/prometheus/cloudwatch_exporter) which retrieves and parses the specified Cloudwatch metrics and translates them into a Prometheus monitoring endpoint.
-Right now, the only AWS resource supported is the Elastic Load Balancer, whose Cloudwatch metrics can be found [here](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-cloudwatch-metrics.html).
+Right now, the only AWS resource supported is the Elastic Load Balancer, whose Cloudwatch metrics are [documented here](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-cloudwatch-metrics.html).
A sample Cloudwatch Exporter configuration file, configured for basic AWS ELB monitoring, is [available for download](../samples/cloudwatch.yml).
diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md
index 8b1cf1a251a..0d300d3c418 100644
--- a/doc/user/project/integrations/prometheus_library/kubernetes.md
+++ b/doc/user/project/integrations/prometheus_library/kubernetes.md
@@ -36,7 +36,7 @@ In order to isolate and only display relevant CPU and Memory metrics for a given
Instead, the [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) name should begin with [CI_ENVIRONMENT_SLUG](../../../../ci/variables/README.md#predefined-environment-variables). It can be followed by a `-` and additional content if desired. For example, a deployment name of `review-homepage-5620p5` would match the `review/homepage` environment.
-## Displaying Canary metrics **[PREMIUM]**
+## Displaying Canary metrics **(PREMIUM)**
> Introduced in [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15201).
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index bb8d276c2fc..508e72b5753 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -6,7 +6,7 @@ The Slack Notifications Service allows your GitLab project to send events (e.g.
## Slack Configuration
-1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook/).
+1. Sign in to your Slack team and [start a new Incoming WebHooks configuration](https://my.slack.com/services/new/incoming-webhook).
1. Select the Slack channel where notifications will be sent to by default. Click the **Add Incoming WebHooks integration** button to add the configuration.
1. Copy the **Webhook URL**, which we'll use later in the GitLab configuration.
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index 371e78ca3a4..813066c51ac 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -1,4 +1,4 @@
-# Slack slash commands **[CORE ONLY]**
+# Slack slash commands **(CORE ONLY)**
> Introduced in GitLab 8.15.
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 04a9d9568ca..30940b65454 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -24,7 +24,7 @@ to the webhook URL.
In most cases, you'll need to set up your own [webhook receiver](#example-webhook-receiver)
to receive information from GitLab, and send it to another app, according to your needs.
-We already have a [built-in receiver](https://docs.gitlab.com/ce/project_services/slack.html)
+We already have a [built-in receiver](slack.md)
for sending [Slack](https://api.slack.com/incoming-webhooks) notifications _per project_.
## Overview
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 31020de5208..3eb5581912a 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -28,11 +28,11 @@ Issue Boards** (version introduced in GitLab 8.11 - August 2016).
### Advanced features of Issue Boards
With [GitLab Starter](https://about.gitlab.com/pricing/), you can create
-[multiple issue boards](#multiple-issue-boards-starter) for a given project. **[STARTER]**
+[multiple issue boards](#multiple-issue-boards-starter) for a given project. **(STARTER)**
With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple
issue boards for your groups, and add lists for [assignees](#assignee-lists-premium) and
-[milestones](#milestone-lists-premium). **[PREMIUM]**
+[milestones](#milestone-lists-premium). **(PREMIUM)**
Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards)
below.
@@ -163,7 +163,7 @@ on the following sections.
For a collection of [features per tier](#summary-of-features-per-tier), check the summary below.
-### Multiple Issue Boards **[STARTER]**
+### Multiple Issue Boards **(STARTER)**
> Introduced in [GitLab Enterprise Edition 8.13](https://about.gitlab.com/2016/10/22/gitlab-8-13-released/#multiple-issue-boards-ee).
@@ -188,7 +188,7 @@ NOTE: **Note:**
The Multiple Issue Boards feature is available for
**projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**.
-### Configurable Issue Boards **[STARTER]**
+### Configurable Issue Boards **(STARTER)**
> Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration).
@@ -208,7 +208,7 @@ If you don't have editing permission in a board, you're still able to see the co
![Viewing board configuration](img/issue_board_view_scope.png)
-### Focus mode **[STARTER]**
+### Focus mode **(STARTER)**
> Introduced in [GitLab Starter 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep).
@@ -216,7 +216,7 @@ Click the button at the top right to toggle focus mode on and off. In focus mode
![Board focus mode](img/issue_board_focus_mode.gif)
-### Sum of Issue Weights **[STARTER]**
+### Sum of Issue Weights **(STARTER)**
The top of each list indicates the sum of issue weights for the issues that
belong to that list. This is useful when using boards for capacity allocation,
@@ -224,7 +224,7 @@ especially in combination with [assignee lists](#assignee-lists-premium).
![Issue Board summed weights](img/issue_board_summed_weights.png)
-### Group Issue Boards **[PREMIUM]**
+### Group Issue Boards **(PREMIUM)**
> Introduced in [GitLab Premium 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
@@ -240,7 +240,7 @@ one group issue board per group was made available in GitLab 10.6 Core.
![Group issue board](img/group_issue_board.png)
-### Assignee lists **[PREMIUM]**
+### Assignee lists **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5784) in GitLab 11.0 Premium.
@@ -259,7 +259,7 @@ To remove an assignee list, just as with a label list, click the trash icon.
![Assignee lists](img/issue_board_assignee_lists.png)
-### Milestone lists **[PREMIUM]**
+### Milestone lists **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/6469) in GitLab 11.2 Premium.
diff --git a/doc/user/project/issues/automatic_issue_closing.md b/doc/user/project/issues/automatic_issue_closing.md
index c3e06b219ff..dab79327d6a 100644
--- a/doc/user/project/issues/automatic_issue_closing.md
+++ b/doc/user/project/issues/automatic_issue_closing.md
@@ -1,63 +1,5 @@
-# Automatic issue closing
-
->**Notes:**
->
-> - This is the user docs. In order to change the default issue closing pattern,
-> follow the steps in the [administration docs].
-> - For performance reasons, automatic issue closing is disabled for the very
-> first push from an existing repository.
-
-When a commit or merge request resolves one or more issues, it is possible to
-automatically have these issues closed when the commit or merge request lands
-in the project's default branch.
-
-If a commit message or merge request description contains a sentence matching
-a certain regular expression, all issues referenced from the matched text will
-be closed. This happens when the commit is pushed to a project's
-[**default** branch](../repository/branches/index.md#default-branch), or when a
-commit or merge request is merged into it.
-
-## Default closing pattern value
-
-When not specified, the default issue closing pattern as shown below will be
-used:
-
-```bash
-((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)
-```
-
-Note that `%{issue_ref}` is a complex regular expression defined inside GitLab's
-source code that can match references to:
-
-- A local issue (`#123`).
-- A cross-project issue (`group/project#123`).
-- A link to an issue
- (`https://gitlab.example.com/group/project/issues/123`).
-
---
-
-This translates to the following keywords:
-
-- Close, Closes, Closed, Closing, close, closes, closed, closing
-- Fix, Fixes, Fixed, Fixing, fix, fixes, fixed, fixing
-- Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
-- Implement, Implements, Implemented, Implementing, implement, implements, implemented, implementing
-
+redirect_to: 'managing_issues.md#closing-issues-automatically'
---
-For example the following commit message:
-
-```
-Awesome commit message
-
-Fix #20, Fixes #21 and Closes group/otherproject#22.
-This commit is also related to #17 and fixes #18, #19
-and https://gitlab.example.com/group/otherproject/issues/23.
-```
-
-will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed
-to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as
-it does not match the pattern. It works with multi-line commit messages as well
-as one-liners when used with `git commit -m`.
-
-[administration docs]: ../../../administration/issue_closing_pattern.md
+This document was moved to [another location](managing_issues.md#closing-issues-automatically).
diff --git a/doc/user/project/issues/closing_issues.md b/doc/user/project/issues/closing_issues.md
index 1d88745af9f..04f1c8e1a4a 100644
--- a/doc/user/project/issues/closing_issues.md
+++ b/doc/user/project/issues/closing_issues.md
@@ -1,59 +1,5 @@
-# Closing Issues
+---
+redirect_to: 'managing_issues.md#closing-issues'
+---
-Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
-
-## Directly
-
-Whenever you decide that's no longer need for that issue,
-close the issue using the close button:
-
-![close issue - button](img/button_close_issue.png)
-
-## Via Merge Request
-
-When a merge request resolves the discussion over an issue, you can
-make it close that issue(s) when merged.
-
-All you need is to use a [keyword](automatic_issue_closing.md)
-accompanying the issue number, add to the description of that MR.
-
-In this example, the keyword "closes" prefixing the issue number will create a relationship
-in such a way that the merge request will close the issue when merged.
-
-Mentioning various issues in the same line also works for this purpose:
-
-```md
-Closes #333, #444, #555 and #666
-```
-
-If the issue is in a different repository rather then the MR's,
-add the full URL for that issue(s):
-
-```md
-Closes #333, #444, and https://gitlab.com/<username>/<projectname>/issues/<xxx>
-```
-
-All the following keywords will produce the same behaviour:
-
-- Close, Closes, Closed, Closing, close, closes, closed, closing
-- Fix, Fixes, Fixed, Fixing, fix, fixes, fixed, fixing
-- Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
-
-![merge request closing issue when merged](img/merge_request_closes_issue.png)
-
-If you use any other word before the issue number, the issue and the MR will
-link to each other, but the MR will NOT close the issue(s) when merged.
-
-![mention issues in MRs - closing and related](img/closing_and_related_issues.png)
-
-## From the Issue Board
-
-You can close an issue from [Issue Boards](../issue_board.md) by dragging an issue card
-from its list and dropping into **Closed**.
-
-![close issue from the Issue Board](img/close_issue_from_board.gif)
-
-## Customizing the issue closing pattern
-
-Alternatively, a GitLab **administrator** can
-[customize the issue closing pattern](../../../administration/issue_closing_pattern.md).
+This document was moved to [another location](managing_issues.md#closing-issues).
diff --git a/doc/user/project/issues/create_new_issue.md b/doc/user/project/issues/create_new_issue.md
index c2916c79876..8eec29716c1 100644
--- a/doc/user/project/issues/create_new_issue.md
+++ b/doc/user/project/issues/create_new_issue.md
@@ -1,104 +1,5 @@
-# Create a new Issue
+---
+redirect_to: 'managing_issues.md#create-a-new-issue'
+---
-Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
-
-When you create a new issue, you'll be prompted to fill in
-the information illustrated on the image below.
-
-![New issue from the issues list](img/new_issue.png)
-
-Read through the [issue data and actions documentation](issue_data_and_actions.md#parts-of-an-issue)
-to understand these fields one by one.
-
-## New issue from the Issue Tracker
-
-Navigate to your **Project's Dashboard** > **Issues** > **New Issue** to create a new issue:
-
-![New issue from the issue list view](img/new_issue_from_tracker_list.png)
-
-## New issue from an opened issue
-
-From an **opened issue** in your project, click **New Issue** to create a new
-issue in the same project:
-
-![New issue from an open issue](img/new_issue_from_open_issue.png)
-
-## New issue from the project's dashboard
-
-From your **Project's Dashboard**, click the plus sign (**+**) to open a dropdown
-menu with a few options. Select **New Issue** to create an issue in that project:
-
-![New issue from a project's dashboard](img/new_issue_from_projects_dashboard.png)
-
-## New issue from the Issue Board
-
-From an Issue Board, create a new issue by clicking on the plus sign (**+**) on the top of a list.
-It opens a new issue for that project labeled after its respective list.
-
-![From the issue board](img/new_issue_from_issue_board.png)
-
-## New issue via email
-
-At the bottom of a project's Issues List page, a link to **Email a new issue to this project**
-is displayed if your GitLab instance has [incoming email](../../../administration/incoming_email.md) configured.
-
-![Bottom of a project issues page](img/new_issue_from_email.png)
-
-When you click this link, an email address is displayed which belongs to you for creating issues in this project.
-You can save this address as a contact in your email client for easy acceess.
-
-CAUTION: **Caution:**
-This is a private email address, generated just for you. **Keep it to yourself**,
-as anyone who gets ahold of it can create issues or merge requests as if they
-were you. If the address is compromised, or you'd like it to be regenerated for
-any reason, click **Email a new issue to this project** again and click the reset link.
-
-Sending an email to this address will create a new issue on your behalf for
-this project, where:
-
-- The email subject becomes the issue title.
-- The email body becomes the issue description.
-- [Markdown](../../markdown.md) and [quick actions](../quick_actions.md) are supported.
-
-NOTE: **Note:**
-In GitLab 11.7, we updated the format of the generated email address.
-However the older format is still supported, allowing existing aliases
-or contacts to continue working._
-
-## New issue via Service Desk **[PREMIUM]**
-
-Enable [Service Desk](../service_desk.md) to your project and offer email support.
-By doing so, when your customer sends a new email, a new issue can be created in
-the appropriate project and followed up from there.
-
-## New issue from the group-level Issue Tracker
-
-Head to the Group dashboard and click "Issues" in the sidebar to visit the Issue Tracker
-for all projects in your Group. Select the project you'd like to add an issue for
-using the dropdown button at the top-right of the page.
-
-![Select project to create issue](img/select_project_from_group_level_issue_tracker.png)
-
-We'll keep track of the project you selected most recently, and use it as the default
-for your next visit. This should save you a lot of time and clicks, if you mostly
-create issues for the same project.
-
-![Create issue from group-level issue tracker](img/create_issue_from_group_level_issue_tracker.png)
-
-## New issue via URL with prefilled fields
-
-You can link directly to the new issue page for a given project, with prefilled
-field values using query string parameters in a URL. This is useful for embedding
-a URL in an external HTML page, and also certain scenarios where you want the user to
-create an issue with certain fields prefilled.
-
-The title, description, and description template fields can be prefilled using
-this method. The description and description template fields cannot be pre-entered
-in the same URL (since a description template just populates the description field).
-
-Follow these examples to form your new issue URL with prefilled fields.
-
-- For a new issue in the GitLab Community Edition project with a pre-entered title
- and a pre-entered description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
-- For a new issue in the GitLab Community Edition project with a pre-entered title
- and a pre-entered description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
+This document was moved to [another location](managing_issues.md#create-a-new-issue).
diff --git a/doc/user/project/issues/crosslinking_issues.md b/doc/user/project/issues/crosslinking_issues.md
index ff5b1f2ce50..93dc2a2e4ca 100644
--- a/doc/user/project/issues/crosslinking_issues.md
+++ b/doc/user/project/issues/crosslinking_issues.md
@@ -25,9 +25,8 @@ git commit -m "this is my commit message. Related to https://gitlab.com/<usernam
Of course, you can replace `gitlab.com` with the URL of your own GitLab instance.
-**Note:** Linking your first commit to your issue is going to be relevant
-for tracking your process far ahead with
-[GitLab Cycle Analytics](https://about.gitlab.com/features/cycle-analytics/)).
+NOTE: **Note:** Linking your first commit to your issue is going to be relevant
+for tracking your process with [GitLab Cycle Analytics](https://about.gitlab.com/features/cycle-analytics/).
It will measure the time taken for planning the implementation of that issue,
which is the time between creating an issue and making the first commit.
@@ -35,14 +34,13 @@ which is the time between creating an issue and making the first commit.
Mentioning related issues in merge requests and other issues is useful
for your team members and collaborators to know that there are opened
-issues around that same idea.
+issues regarding the same topic.
-You do that as explained above, when
-[mentioning an issue from a commit message](#from-commit-messages).
+You do that as explained above, when [mentioning an issue from a commit message](#from-commit-messages).
-When mentioning the issue "A" in issue "B", the issue "A" will also
-display a notification in its tracker. The same is valid for mentioning
-issues in merge requests.
+When mentioning issue `#111` in issue `#222`, issue `#111` will also display a notification
+in its tracker. That is, you only need to mention the relationship once for it to
+display in both issues. The same is valid when mentioning issues in [merge requests](#from-merge-requests).
![issue mentioned in issue](img/mention_in_issue.png)
@@ -53,10 +51,7 @@ they do for [related issues](#from-related-issues).
When you mention an issue in a merge request description, it will simply
[link the issue and merge request together](#from-related-issues). Additionally,
-you can also [set an issue to close as soon as the merge request is merged](closing_issues.md#via-merge-request).
+you can also [set an issue to close automatically](managing_issues.md#closing-issues-automatically)
+as soon as the merge request is merged.
![issue mentioned in MR](img/mention_in_merge_request.png)
-
-### Close an issue by merging a merge request
-
-To [close an issue when a merge request is merged](closing_issues.md#via-merge-request), use the [automatic issue closing pattern](automatic_issue_closing.md).
diff --git a/doc/user/project/issues/csv_export.md b/doc/user/project/issues/csv_export.md
index 56b94585672..0b7a7af5927 100644
--- a/doc/user/project/issues/csv_export.md
+++ b/doc/user/project/issues/csv_export.md
@@ -1,4 +1,4 @@
-# Export Issues to CSV **[STARTER]**
+# Export Issues to CSV **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1126) in [GitLab Starter 9.0](https://about.gitlab.com/2017/03/22/gitlab-9-0-released/#export-issues-ees-eep).
diff --git a/doc/user/project/issues/csv_import.md b/doc/user/project/issues/csv_import.md
index b0b1cfcfdf7..cc0d5ac9028 100644
--- a/doc/user/project/issues/csv_import.md
+++ b/doc/user/project/issues/csv_import.md
@@ -7,7 +7,8 @@ Issues can be imported to a project by uploading a CSV file with the columns
The user uploading the CSV file will be set as the author of the imported issues.
-> **Note:** A permission level of `Developer` or higher is required to import issues.
+NOTE: **Note:** A permission level of [Developer](../../permissions.md), or higher, is required
+to import issues.
## Prepare for the import
@@ -24,42 +25,26 @@ To import issues:
1. Select the file and click the **Import issues** button.
The file is processed in the background and a notification email is sent
-to you once the import is completed.
+to you once the import is complete.
## CSV file format
-### Header row
+When importing issues from a CSV file, it must be formatted in a certain way:
-CSV files must contain a header row where the first column header is `title` and the second is `description`.
-If additional columns are present, they will be ignored.
+- **header row:** CSV files must contain a header row where the first column header
+ is `title` and the second is `description`. If additional columns are present, they
+ will be ignored.
+- **separators:** The column separator is automatically detected from the header row.
+ Supported separator characters are: commas (`,`), semicolons (`;`), and tabs (`\t`).
+ The row separator can be either `CRLF` or `LF`.
+- **double-quote character:** The double-quote (`"`) character is used to quote fields,
+ enabling the use of the column separator within a field (see the third line in the
+ sample CSV data below). To insert a double-quote (`"`) within a quoted
+ field, use two double-quote characters in succession, i.e. `""`.
+- **data rows:** After the header row, succeeding rows must follow the same column
+ order. The issue title is required while the description is optional.
-### Column separator
-
-The column separator is automatically detected from the header row.
-
-Supported separator characters are: commas (`,`), semicolons (`;`), and tabs (`\t`).
-
-### Row separator
-
-Lines ending in either `CRLF` or `LF` are supported.
-
-### Quote character
-
-The double-quote (`"`) character is used to quote fields so you can use the column separator within a field. To insert
-a double-quote (`"`) within a quoted field, use two double-quote characters in succession, i.e. `""`.
-
-### Data rows
-
-After the header row, succeeding rows must follow the same column order. The issue title is required while the
-description is optional.
-
-### File size
-
-The limit depends on the configuration value of Max Attachment Size for the GitLab instance.
-
-For GitLab.com, it is set to 10 MB.
-
-## Sample data
+Sample CSV data:
```csv
title,description
@@ -67,3 +52,9 @@ My Issue Title,My Issue Description
Another Title,"A description, with a comma"
"One More Title","One More Description"
```
+
+### File size
+
+The limit depends on the configuration value of Max Attachment Size for the GitLab instance.
+
+For GitLab.com, it is set to 10 MB.
diff --git a/doc/user/project/issues/deleting_issues.md b/doc/user/project/issues/deleting_issues.md
index 536a0de8974..e50259e0dcf 100644
--- a/doc/user/project/issues/deleting_issues.md
+++ b/doc/user/project/issues/deleting_issues.md
@@ -1,13 +1,5 @@
-# Deleting Issues
+---
+redirect_to: 'managing_issues.md#deleting-issues'
+---
-> [Introduced][ce-2982] in GitLab 8.6
-
-Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
-
-You can delete an issue by editing it and clicking on the delete button.
-
-![delete issue - button](img/delete_issue.png)
-
->**Note:** Only [project owners](../../permissions.md) can delete issues.
-
-[ce-2982]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2982 \ No newline at end of file
+This document was moved to [another location](managing_issues.md#deleting-issues).
diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md
index 987c16dfab6..bd3298497d2 100644
--- a/doc/user/project/issues/due_dates.md
+++ b/doc/user/project/issues/due_dates.md
@@ -4,30 +4,32 @@
Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
-Due dates can be used in issues to keep track of deadlines and make sure
-features are shipped on time. Due dates require at least [Reporter permissions](../../permissions.md#project-members-permissions)
-to be able to edit them. On the contrary, they can be seen by everybody.
+Due dates can be used in issues to keep track of deadlines and make sure features are
+shipped on time. Users must have at least [Reporter permissions](../../permissions.md)
+to be able to edit them, but they can be seen by everybody with permission to view
+the issue.
## Setting a due date
-When creating or editing an issue, you can see the due date field from where
-a calendar will appear to help you choose the date you want. To remove it,
-select the date text and delete it.
+When creating or editing an issue, you can click in the **due date** field and a calendar
+will appear to help you choose the date you want. To remove the date, select the date
+text and delete it. The date is related to the server's timezone, not the timezone of
+the user setting the due date.
![Create a due date](img/due_dates_create.png)
-A quicker way to set a due date is via the issue sidebar. Simply expand the
-sidebar and select **Edit** to pick a due date or remove the existing one.
+You can also set a due date via the issue sidebar. Expand the
+sidebar and click **Edit** to pick a due date or remove the existing one.
Changes are saved immediately.
![Edit a due date via the sidebar](img/due_dates_edit_sidebar.png)
## Making use of due dates
-Issues that have a due date can be distinctively seen in the issue tracker
+Issues that have a due date can be easily seen in the issue tracker,
displaying a date next to them. Issues where the date is overdue will have
the icon and the date colored red. You can sort issues by those that are
-_Due soon_ or _Due later_ from the dropdown menu in the right.
+`Due soon` or `Due later` from the dropdown menu on the right.
![Issues with due dates in the issues index page](img/due_dates_issues_index_page.png)
@@ -36,14 +38,13 @@ Due dates also appear in your [todos list](../../../workflow/todos.md).
![Issues with due dates in the todos](img/due_dates_todos.png)
The day before an open issue is due, an email will be sent to all participants
-of the issue. Both the due date and the day before are calculated using the
+of the issue. Like the due date, the "day before the due date" is determined by the
server's timezone.
Issues with due dates can also be exported as an iCalendar feed. The URL of the
feed can be added to calendar applications. The feed is accessible by clicking
-on the _Subscribe to calendar_ button on the following pages:
+on the **Subscribe to calendar** button on the following pages:
-- on the **Assigned Issues** page that is linked on the right-hand side of the
- GitLab header
+- on the **Assigned Issues** page that is linked on the right-hand side of the GitLab header
- on the **Project Issues** page
- on the **Group Issues** page
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 4acbb4cc3f6..e917697e973 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -6,8 +6,9 @@ Issues are the fundamental medium for collaborating on ideas and planning work i
The GitLab issue tracker is an advanced tool for collaboratively developing ideas, solving problems, and planning work.
-Issues can allow you, your team, and your collaborators to share and discuss proposals before and during their implementation.
-However, they can be used for a variety of other purposes, customized to your needs and workflow.
+Issues can allow you, your team, and your collaborators to share and discuss proposals
+before, and during, their implementation. However, they can be used for a variety of
+other purposes, customized to your needs and workflow.
Issues are always associated with a specific project, but if you have multiple projects in a group,
you can also view all the issues collectively at the group level.
@@ -17,13 +18,15 @@ you can also view all the issues collectively at the group level.
- Discussing the implementation of a new idea
- Tracking tasks and work status
- Accepting feature proposals, questions, support requests, or bug reports
-- Elaborating new code implementations
+- Elaborating on new code implementations
-See also the blog post "[Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)".
+See also [Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/).
## Parts of an issue
-Issues contain a variety of content and metadata, enabling a large range of flexibility in how they are used. Each issue can contain the following attributes, though some items may remain unset.
+Issues contain a variety of content and metadata, enabling a large range of flexibility
+in how they are used. Each issue can contain the following attributes, though not all items
+must be set.
<table class="borderless-table fixed-table">
<tr>
@@ -70,23 +73,36 @@ Issues contain a variety of content and metadata, enabling a large range of flex
## Viewing and managing issues
-While you can view and manage the full detail of an issue at its URL, you can also work with multiple issues at a time using the Issues List, Issue Boards, Epics **[ULTIMATE]**, and issue references.
+While you can view and manage the full details of an issue on the [issue page](#issue-page),
+you can also work with multiple issues at a time using the [Issues List](#issues-list),
+[Issue Boards](#issue-boards), Issue references, and [Epics](#epics-ultimate)**(ULTIMATE)**.
+
+Key actions for Issues include:
+
+- [Creating issues](managing_issues.md#create-a-new-issue)
+- [Moving issues](managing_issues.md#moving-issues)
+- [Closing issues](managing_issues.md#closing-issues)
+- [Deleting issues](managing_issues.md#deleting-issues)
### Issue page
![Issue view](img/issues_main_view.png)
-On an issue’s page, you can view all aspects of the issue, and you can also modify them if you you have the necessary [permissions](../../permissions.md).
-
-For more information, see the [Issue Data and Actions](issue_data_and_actions.md) page.
+On an issue's page, you can view [all aspects of the issue](issue_data_and_actions.md),
+and modify them if you you have the necessary [permissions](../../permissions.md).
### Issues list
![Project issues list view](img/project_issues_list_view.png)
-On the Issues List, you can view all issues in the current project, or from multiple projects when opening the Issues List from the higher-level group context. Filter the issue list by [any search query](../../search/index.md#issues-and-merge-requests-per-project) and/or specific metadata, such as label(s), assignees(s), status, and more. From this view, you can also make certain changes [in bulk](../bulk_editing.md) to the displayed issues.
+On the Issues List, you can view all issues in the current project, or from multiple
+projects when opening the Issues List from the higher-level group context. Filter the
+issue list with a [search query](../../search/index.md#issues-and-merge-requests-per-project),
+including specific metadata, such as label(s), assignees(s), status, and more. From this
+view, you can also make certain changes [in bulk](../bulk_editing.md) to the displayed issues.
-For more information on interacting with Issues, see the [Issue Data and Actions](issue_data_and_actions.md) page.
+For more information, see the [Issue Data and Actions](issue_data_and_actions.md) page
+for a rundown of all the fields and information in an issue.
For sorting by issue priority, see [Label Priority](../labels.md#label-priority).
@@ -94,44 +110,55 @@ For sorting by issue priority, see [Label Priority](../labels.md#label-priority)
![Issue board](img/issue_board.png)
-Issue boards are Kanban boards with columns that display issues based on their labels or their assignees**[PREMIUM]**. They offer the flexibility to manage issues using highly customizable workflows.
-
-You can reorder issues within a column, or drag an issue card to another column; its associated label or assignee will change to match that of the new column. The entire board can also be filtered to only include issues from a certain milestone or an overarching label.
+[Issue boards](../issue_board.md) are Kanban boards with columns that display issues based on their labels
+or their assignees**(PREMIUM)**. They offer the flexibility to manage issues using
+highly customizable workflows.
-For more information, see the [Issue Boards](../issue_board.md) page.
+You can reorder issues within a column. If you drag an issue card to another column, its
+associated label or assignee will change to match that of the new column. The entire
+board can also be filtered to only include issues from a certain milestone or an overarching
+label.
-### Epics **[ULTIMATE]**
+### Epics **(ULTIMATE)**
-Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones.
+[Epics](../../group/epics/index.md) let you manage your portfolio of projects more
+efficiently and with less effort by tracking groups of issues that share a theme, across
+projects and milestones.
-For more information, see the [Epics](../../group/epics/index.md) page.
+### Related issues **(STARTER)**
-### Related issues **[STARTER]**
+You can mark two issues as related, so that when viewing one, the other is always
+listed in its [Related Issues](related_issues.md) section. This can help display important
+context, such as past work, dependencies, or duplicates.
-You can mark two issues as related, so that when viewing each one, the other is always listed in its Related Issues section. This can help display important context, such as past work, dependencies, or duplicates.
+### Crosslinking issues
-For more information, see [Related Issues](related_issues.md).
+You can [crosslink issues](crosslinking_issues.md) by referencing an issue from another
+issue or merge request by including its URL or ID. The referenced issue displays a
+message in the Activity stream about the reference, with a link to the other issue or MR.
-### Crosslinking issues
+### Similar issues
-When you reference an issue from another issue or merge request by including its URL or ID, the referenced issue displays a message in the Activity stream about the reference, with a link to the other issue or MR.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22866) in GitLab 11.6.
-For more information, see [Crosslinking issues](crosslinking_issues.md).
+To prevent duplication of issues for the same topic, GitLab searches for similar issues
+when new issues are being created.
-## Issue actions
+When typing in the title in the **New Issue** page, GitLab searches titles and descriptions
+across all issues the user has access to in the current project. Up 5 similar issues,
+sorted by most recently updated, are displayed below the title box. Note that this feature
+requires [GraphQL](../../../api/graphql/index.md) to be enabled.
-- [Create an issue](create_new_issue.md)
-- [Create an issue from a template](../../project/description_templates.md#using-the-templates)
-- [Close an issue](closing_issues.md)
-- [Move an issue](moving_issues.md)
-- [Delete an issue](deleting_issues.md)
-- [Create a merge request from an issue](issue_data_and_actions.md#22-create-merge-request)
+![Similar issues](img/similar_issues.png)
-## Advanced issue management
+## Other Issue actions
-- [Bulk edit issues](../bulk_editing.md) - From the Issues List, select multiple issues in order to change their status, assignee, milestone, or labels in bulk.
+- [Create an issue from a template](../../project/description_templates.md#using-the-templates)
+- [Set a due date](due_dates.md)
+- [Bulk edit issues](../bulk_editing.md) - From the Issues List, select multiple issues
+ in order to change their status, assignee, milestone, or labels in bulk.
- [Import issues](csv_import.md)
-- [Export issues](csv_export.md) **[STARTER]**
+- [Export issues](csv_export.md) **(STARTER)**
- [Issues API](../../../api/issues.md)
-- Configure an [external issue tracker](../../../integration/external-issue-tracker.md) such as Jira, Redmine,
- or Bugzilla.
+- Configure an [external issue tracker](../../../integration/external-issue-tracker.md)
+ such as Jira, Redmine, or Bugzilla.
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index 2103f331aa2..9c4d0de46d3 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -39,16 +39,19 @@ after it is closed.
![Report Abuse](img/report-abuse.png)
-#### 2. Todos
+#### 2. To Do
-You can click **add todo** to add the issue to your [GitLab Todo](../../../workflow/todos.md)
-list. If it is already on your todo list, the buttom will show **mark todo as done**,
-which you can click to mark that issue as done (which will be reflected in the Todo list).
+You can add issues to and remove issues from your [GitLab To-Do List](../../../workflow/todos.md).
+
+The button to do this has a different label depending on whether the issue is already on your To-Do List or not. If the issue is:
+
+- Already on your To-Do List: The button is labeled **Mark as done**. Click the button to remove the issue from your To-Do List.
+- Not on your To-Do List: The button is labelled **Add a To Do**. Click the button to add the issue to your To-Do List.
#### 3. Assignee
An issue can be assigned to yourself, another person, or [many people](#31-multiple-assignees-STARTER).
-The assignee(s) can be changed as much as needed. The idea is that the assignees are
+The assignee(s) can be changed as often as needed. The idea is that the assignees are
responsible for that issue until it's reassigned to someone else to take it from there.
When assigned to someone, it will appear in their assigned issues list.
@@ -56,7 +59,7 @@ TIP: **Tip:**
If a user is not member of that project, it can only be
assigned to them if they created the issue themselves.
-##### 3.1. Multiple Assignees **[STARTER]**
+##### 3.1. Multiple Assignees **(STARTER)**
Often multiple people work on the same issue together, which can be especially difficult
to track in large teams where there is shared ownership of an issue.
@@ -64,7 +67,7 @@ to track in large teams where there is shared ownership of an issue.
In [GitLab Starter](https://about.gitlab.com/pricing/), you can
[assign multiple people](multiple_assignees_for_issues.md) to an issue.
-#### 4. Epic **[ULTIMATE]**
+#### 4. Epic **(ULTIMATE)**
You can assign issues to an [Epic](../../group/epics/index.md), which allows better
management of groups of related issues.
@@ -99,7 +102,7 @@ TIP: **Tip:**
If a label doesn't exist yet, you can click **Edit**, and it opens a dropdown menu
from which you can select **Create new label**.
-#### 9. Weight **[STARTER]**
+#### 9. Weight **(STARTER)**
[Assign a weight](../../../workflow/issue_weight.md) to an issue.
Larger values are used to indicate more effort is required to complete the issue. Only
@@ -177,7 +180,7 @@ TIP: **Tip:**
Avoid mentioning `@all` in issues and merge requests, as it sends an email notification
to all the members of that project's group, which can be interpreted as spam.
-#### 18. Related Issues **[STARTER]**
+#### 18. Related Issues **(STARTER)**
Issues that were mentioned as [related issues](related_issues.md) are listed here.
You can also click the `+` to add more related issues.
@@ -206,6 +209,14 @@ You can filter what is displayed in the issue history by clicking on **Show all
and selecting either **Show comments only**, which only shows discussions and hides
updates to the issue, or **Show history only**, which hides discussions and only shows updates.
+- You can mention a user or a group present in your GitLab instance with
+ `@username` or `@groupname` and they will be notified via To-Do items
+ and email, unless they have [disabled all notifications](#13-notifications)
+ in their profile settings.
+- Mentions for yourself (the current logged in user), will be highlighted
+ in a different color, allowing you to easily see which comments involve you,
+ helping you focus on them quickly.
+
![Show all activity](img/show-all-activity.png)
#### 22. Create Merge Request
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
new file mode 100644
index 00000000000..709588959c1
--- /dev/null
+++ b/doc/user/project/issues/managing_issues.md
@@ -0,0 +1,225 @@
+# Managing Issues
+
+[GitLab Issues](index.md) are the fundamental medium for collaborating on ideas and
+planning work in GitLab. [Creating](#create-a-new-issue), [moving](#moving-issues),
+[closing](#closing-issues), and [deleting](#deleting-issues) are key actions that
+you can do with issues.
+
+## Create a new Issue
+
+When you create a new issue, you'll be prompted to fill in the [data and fields of the issue](issue_data_and_actions.md#parts-of-an-issue), as illustrated below.
+
+![New issue from the issues list](img/new_issue.png)
+
+### Accessing the new Issue form
+
+There are many ways to get to the new Issue form from within a project:
+
+- Navigate to your **Project's Dashboard** > **Issues** > **New Issue**:
+
+ ![New issue from the issue list view](img/new_issue_from_tracker_list.png)
+
+- From an **opened issue** in your project, click **New Issue** to create a new
+ issue in the same project:
+
+ ![New issue from an open issue](img/new_issue_from_open_issue.png)
+
+- From your **Project's Dashboard**, click the plus sign (**+**) to open a dropdown
+ menu with a few options. Select **New Issue** to create an issue in that project:
+
+ ![New issue from a project's dashboard](img/new_issue_from_projects_dashboard.png)
+
+- From an **Issue Board**, create a new issue by clicking on the plus sign (**+**) at the top of a list.
+ It opens a new issue for that project, pre-labeled with its respective list.
+
+ ![From the issue board](img/new_issue_from_issue_board.png)
+
+### New issue from the group-level Issue Tracker
+
+Go to the Group dashboard and click "Issues" in the sidebar to visit the Issue Tracker
+for all projects in your Group. Select the project you'd like to add an issue for
+using the dropdown button at the top-right of the page.
+
+![Select project to create issue](img/select_project_from_group_level_issue_tracker.png)
+
+We'll keep track of the project you selected most recently, and use it as the default
+for your next visit. This should save you a lot of time and clicks, if you mostly
+create issues for the same project.
+
+![Create issue from group-level issue tracker](img/create_issue_from_group_level_issue_tracker.png)
+
+### New issue via Service Desk **(PREMIUM)**
+
+Enable [Service Desk](../service_desk.md) for your project and offer email support.
+By doing so, when your customer sends a new email, a new issue can be created in
+the appropriate project and followed up from there.
+
+### New issue via email
+
+A link to **Email a new issue to this project** is displayed at the bottom of a project's
+**Issues List** page, if your GitLab instance has [incoming email](../../../administration/incoming_email.md)
+configured.
+
+![Bottom of a project issues page](img/new_issue_from_email.png)
+
+When you click this link, an email address is generated and displayed, which should be used
+by **you only**, to create issues in this project. You can save this address as a
+contact in your email client for easy acceess.
+
+CAUTION: **Caution:**
+This is a private email address, generated just for you. **Keep it to yourself**,
+as anyone who knows it can create issues or merge requests as if they
+were you. If the address is compromised, or you'd like it to be regenerated for
+any reason, click **Email a new issue to this project** again and click the reset link.
+
+Sending an email to this address will create a new issue in your name for
+this project, where:
+
+- The email subject becomes the issue title.
+- The email body becomes the issue description.
+- [Markdown](../../markdown.md) and [quick actions](../quick_actions.md) are supported.
+
+NOTE: **Note:**
+In GitLab 11.7, we updated the format of the generated email address. However the
+older format is still supported, allowing existing aliases or contacts to continue working.
+
+### New issue via URL with prefilled fields
+
+You can link directly to the new issue page for a given project, with prefilled
+field values using query string parameters in a URL. This is useful for embedding
+a URL in an external HTML page, and also certain scenarios where you want the user to
+create an issue with certain fields prefilled.
+
+The title, description, and description template fields can be prefilled using
+this method. You cannot pre-fill both the description and description template fields
+in the same URL (since a description template also populates the description field).
+
+Follow these examples to form your new issue URL with prefilled fields.
+
+- For a new issue in the GitLab Community Edition project with a pre-filled title
+ and a pre-filled description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
+- For a new issue in the GitLab Community Edition project with a pre-filled title
+ and a pre-filled description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
+
+## Moving Issues
+
+Moving an issue will copy it to a new location (project), and close it in the old project,
+but it will not be deleted. There will also be a system note added to both issues
+indicating where it came from and went to.
+
+The "Move issue" button is at the bottom of the right-sidebar when viewing the issue.
+
+![move issue - button](img/sidebar_move_issue.png)
+
+### Moving Issues in Bulk
+
+If you have advanced technical skills you can also bulk move all the issues from one project to another in the rails console. The below script will move all the issues from one project to another that are not in status **closed**.
+
+To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below script. Please be sure to change **project**, **admin_user** and **target_project** to your values. We do also recommend [creating a backup](../../../raketasks/backup_restore.md#creating-a-backup-of-the-gitlab-system) before attempting any changes in the console.
+
+```ruby
+project = Project.find_by_full_path('full path of the project where issues are moved from')
+issues = project.issues
+admin_user = User.find_by_username('username of admin user') # make sure user has permissions to move the issues
+target_project = Project.find_by_full_path('full path of target project where issues moved to')
+
+issues.each do |issue|
+ if issue.state != "closed" && issue.moved_to.nil?
+ Issues::MoveService.new(project, admin_user).execute(issue, target_project)
+ else
+ puts "issue with id: #{issue.id} and title: #{issue.title} was not moved"
+ end
+end; nil
+```
+
+## Closing Issues
+
+When you decide that an issue is resolved, or no longer needed, you can close the issue
+using the close button:
+
+![close issue - button](img/button_close_issue.png)
+
+You can also close an issue from the [Issue Boards](../issue_board.md) by dragging an issue card
+from its list and dropping it into the **Closed** list.
+
+![close issue from the Issue Board](img/close_issue_from_board.gif)
+
+### Closing issues automatically
+
+NOTE: **Note:**
+For performance reasons, automatic issue closing is disabled for the very first
+push from an existing repository.
+
+When a commit or merge request resolves one or more issues, it is possible to have
+these issues closed automatically when the commit or merge request reaches the project's
+default branch.
+
+If a commit message or merge request description contains text matching a [defined pattern](#default-closing-pattern),
+all issues referenced in the matched text will be closed. This happens when the commit
+is pushed to a project's [**default** branch](../repository/branches/index.md#default-branch),
+or when a commit or merge request is merged into it.
+
+For example, if `Closes #4, #6, Related to #5` is included in a Merge Request
+description, issues `#4` and `#6` will close automatically when the MR is merged, but not `#5`.
+Using `Related to` flags `#5` as a [related issue](related_issues.md),
+but it will not close automatically.
+
+![merge request closing issue when merged](img/merge_request_closes_issue.png)
+
+If the issue is in a different repository than the MR, add the full URL for the issue(s):
+
+```md
+Closes #4, #6, and https://gitlab.com/<username>/<projectname>/issues/<xxx>
+```
+
+#### Default closing pattern
+
+When not specified, the default issue closing pattern as shown below will be used:
+
+```bash
+((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)
+```
+
+This translates to the following keywords:
+
+- Close, Closes, Closed, Closing, close, closes, closed, closing
+- Fix, Fixes, Fixed, Fixing, fix, fixes, fixed, fixing
+- Resolve, Resolves, Resolved, Resolving, resolve, resolves, resolved, resolving
+- Implement, Implements, Implemented, Implementing, implement, implements, implemented, implementing
+
+Note that `%{issue_ref}` is a complex regular expression defined inside GitLab's
+source code that can match references to:
+
+- A local issue (`#123`).
+- A cross-project issue (`group/project#123`).
+- A link to an issue (`https://gitlab.example.com/group/project/issues/123`).
+
+For example the following commit message:
+
+```
+Awesome commit message
+
+Fix #20, Fixes #21 and Closes group/otherproject#22.
+This commit is also related to #17 and fixes #18, #19
+and https://gitlab.example.com/group/otherproject/issues/23.
+```
+
+will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to,
+as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does
+not match the pattern. It works with multi-line commit messages as well as one-liners
+when used from the command line with `git commit -m`.
+
+#### Customizing the issue closing pattern **(CORE ONLY)**
+
+In order to change the default issue closing pattern, GitLab administrators must edit the
+[`gitlab.rb` or `gitlab.yml` file](../../../administration/issue_closing_pattern.md)
+of your installation.
+
+## Deleting Issues
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2982) in GitLab 8.6
+
+Users with [project owner permission](../../permissions.md) can delete an issue by
+editing it and clicking on the delete button.
+
+![delete issue - button](img/delete_issue.png)
diff --git a/doc/user/project/issues/moving_issues.md b/doc/user/project/issues/moving_issues.md
index 8aac2c01444..8331f865b83 100644
--- a/doc/user/project/issues/moving_issues.md
+++ b/doc/user/project/issues/moving_issues.md
@@ -1,35 +1,5 @@
-# Moving Issues
-
-Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
-
-Moving an issue will close it and duplicate it on the specified project.
-There will also be a system note added to both issues indicating where it came from or went to.
-
-You can move an issue with the "Move issue" button at the bottom of the right-sidebar when viewing the issue.
-
-![move issue - button](img/sidebar_move_issue.png)
-
-## Troubleshooting
-
-### Moving Issues in Bulk
-
-If you have advanced technical skills you can also bulk move all the issues from one project to another in the rails console. The below script will move all the issues from one project to another that are not in status **closed**.
-
-To access rails console run `sudo gitlab-rails console` on the GitLab server and run the below script. Please be sure to change **project**, **admin_user** and **target_project** to your values. We do also recommend [creating a backup](https://docs.gitlab.com/ee/raketasks/backup_restore.html#creating-a-backup-of-the-gitlab-system) before attempting any changes in the console.
-
-```ruby
-project = Project.find_by_full_path('full path of the project where issues are moved from')
-issues = project.issues
-admin_user = User.find_by_username('username of admin user') # make sure user has permissions to move the issues
-target_project = Project.find_by_full_path('full path of target project where issues moved to')
-
-issues.each do |issue|
- if issue.state != "closed" && issue.moved_to.nil?
- Issues::MoveService.new(project, admin_user).execute(issue, target_project)
- else
- puts "issue with id: #{issue.id} and title: #{issue.title} was not moved"
- end
-end; nil
-
-```
+---
+redirect_to: 'managing_issues.md#moving-issues'
+---
+This document was moved to [another location](managing_issues.md#moving-issues).
diff --git a/doc/user/project/issues/multiple_assignees_for_issues.md b/doc/user/project/issues/multiple_assignees_for_issues.md
index d1db0790d69..c9efe9f5031 100644
--- a/doc/user/project/issues/multiple_assignees_for_issues.md
+++ b/doc/user/project/issues/multiple_assignees_for_issues.md
@@ -1,4 +1,4 @@
-# Multiple Assignees for Issues **[STARTER]**
+# Multiple Assignees for Issues **(STARTER)**
> **Note:**
[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1904)
diff --git a/doc/user/project/issues/related_issues.md b/doc/user/project/issues/related_issues.md
index e679ebf86e6..9c72fe33d0d 100644
--- a/doc/user/project/issues/related_issues.md
+++ b/doc/user/project/issues/related_issues.md
@@ -1,4 +1,4 @@
-# Related issues **[STARTER]**
+# Related issues **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1797) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.4.
diff --git a/doc/user/project/issues/similar_issues.md b/doc/user/project/issues/similar_issues.md
index e90ecd88ec6..9cbac53ee41 100644
--- a/doc/user/project/issues/similar_issues.md
+++ b/doc/user/project/issues/similar_issues.md
@@ -1,16 +1,5 @@
-# Similar issues
+---
+redirect_to: 'index.md#similar-issues'
+---
-> [Introduced][ce-22866] in GitLab 11.6.
-
-Similar issues suggests issues that are similar when new issues are being created.
-This features requires [GraphQL] to be enabled.
-
-![Similar issues](img/similar_issues.png)
-
-You can see the similar issues when typing in the title in the new issue form.
-This searches both titles and descriptions across all issues the user has access
-to in the current project. It then displays the first 5 issues sorted by most
-recently updated.
-
-[GraphQL]: ../../../api/graphql/index.md
-[ce-22866]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22866
+This document was moved to [another location](index.md#similar-issues).
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 3eca1313a18..fdfeb4aa4cd 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -11,7 +11,7 @@ In GitLab, you can create project and group labels:
- **Project labels** can be assigned to issues or merge requests in that project only.
- **Group labels** can be assigned to any issue or merge request of any project in that group or any subgroups of the group.
-## Scoped labels **[PREMIUM]**
+## Scoped labels **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/9175) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
@@ -38,7 +38,7 @@ For example, `nested::key1::value1` and `nested::key1::value2` cannot both exist
`nested::key1::value1` and `nested::key2::value1` can both exist on the same issue, as these are considered to use two different label scopes, `nested::key1` and `nested::key2`.
-### Workflows with scoped labels **[PREMIUM]**
+### Workflows with scoped labels **(PREMIUM)**
Suppose you wanted a custom field in issues to track the platform operating system
that your features target, where each issue should only target one platform. You
@@ -144,9 +144,9 @@ From the group epic list page, you can [filter](../search/index.md#issues-and-me
### Filtering in issue boards
- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [search and filter bar](../search/index.md#issue-boards).
-- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **[PREMIUM]**
-- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[STARTER]**
-- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **[STARTER]**
+- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [search and filter bar](../search/index.md#issue-boards). **(PREMIUM)**
+- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **(STARTER)**
+- From [group issue boards](issue_board.md#group-issue-boards-premium), you can filter by only group labels in the [issue board configuration](issue_board.md#configurable-issue-boards-starter). **(STARTER)**
## Subscribing to labels
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index f6c4f767e3c..49b9826a52a 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# Browser Performance Testing **[PREMIUM]**
+# Browser Performance Testing **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3507)
in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3.
@@ -19,7 +19,7 @@ tool for measuring the performance of web sites, and has built a simple
which outputs the results in a file called `performance.json`. This plugin
outputs the performance score for each page that is analyzed.
-The [Sitespeed.io performance score](http://examples.sitespeed.io/6.0/2017-11-23-23-43-35/help.html)
+The [Sitespeed.io performance score](https://examples.sitespeed.io/6.0/2017-11-23-23-43-35/help.html)
is a composite value based on best practices, and we will be expanding support
for [additional metrics](https://gitlab.com/gitlab-org/gitlab-ee/issues/4370)
in a future release.
diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md
index c3c2bdd94b3..ad1d79ae5b1 100644
--- a/doc/user/project/merge_requests/code_quality.md
+++ b/doc/user/project/merge_requests/code_quality.md
@@ -2,7 +2,7 @@
type: reference, howto
---
-# Code Quality **[STARTER]**
+# Code Quality **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1984)
in [GitLab Starter](https://about.gitlab.com/pricing/) 9.3.
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 447b338928c..37a0630d0f3 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -23,7 +23,7 @@ With GitLab merge requests, you can:
- Build, test, and deploy your code in a per-branch basis with built-in [GitLab CI/CD](../../../ci/README.md)
- Prevent the merge request from being merged before it's ready with [WIP MRs](#work-in-progress-merge-requests)
- View the deployment process through [Pipeline Graphs](../../../ci/pipelines.md#visualizing-pipelines)
-- [Automatically close the issue(s)](../../project/issues/closing_issues.md#via-merge-request) that originated the implementation proposed in the merge request
+- [Automatically close the issue(s)](../../project/issues/managing_issues.md#closing-issues-automatically) that originated the implementation proposed in the merge request
- Assign it to any registered user, and change the assignee how many times you need
- Assign a [milestone](../../project/milestones/index.md) and track the development of a broader implementation
- Organize your issues and merge requests consistently throughout the project with [labels](../../project/labels.md)
@@ -37,16 +37,16 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also:
-- Prepare a full review and submit it once it's ready with [Merge Request Reviews](../../discussions/index.md#merge-request-reviews-premium) **[PREMIUM]**
-- View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md) **[PREMIUM]**
-- Request [approvals](merge_request_approvals.md) from your managers **[STARTER]**
-- Analyze the impact of your changes with [Code Quality reports](code_quality.md) **[STARTER]**
-- Manage the licenses of your dependencies with [License Management](../../application_security/license_management/index.md) **[ULTIMATE]**
-- Analyze your source code for vulnerabilities with [Static Application Security Testing](../../application_security/sast/index.md) **[ULTIMATE]**
-- Analyze your running web applications for vulnerabilities with [Dynamic Application Security Testing](../../application_security/dast/index.md) **[ULTIMATE]**
-- Analyze your dependencies for vulnerabilities with [Dependency Scanning](../../application_security/dependency_scanning/index.md) **[ULTIMATE]**
-- Analyze your Docker images for vulnerabilities with [Container Scanning](../../application_security/container_scanning/index.md) **[ULTIMATE]**
-- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing-premium) **[PREMIUM]**
+- Prepare a full review and submit it once it's ready with [Merge Request Reviews](../../discussions/index.md#merge-request-reviews-premium) **(PREMIUM)**
+- View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md) **(PREMIUM)**
+- Request [approvals](merge_request_approvals.md) from your managers **(STARTER)**
+- Analyze the impact of your changes with [Code Quality reports](code_quality.md) **(STARTER)**
+- Manage the licenses of your dependencies with [License Management](../../application_security/license_management/index.md) **(ULTIMATE)**
+- Analyze your source code for vulnerabilities with [Static Application Security Testing](../../application_security/sast/index.md) **(ULTIMATE)**
+- Analyze your running web applications for vulnerabilities with [Dynamic Application Security Testing](../../application_security/dast/index.md) **(ULTIMATE)**
+- Analyze your dependencies for vulnerabilities with [Dependency Scanning](../../application_security/dependency_scanning/index.md) **(ULTIMATE)**
+- Analyze your Docker images for vulnerabilities with [Container Scanning](../../application_security/container_scanning/index.md) **(ULTIMATE)**
+- Determine the performance impact of changes with [Browser Performance Testing](#browser-performance-testing-premium) **(PREMIUM)**
## Use cases
@@ -54,9 +54,9 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
-1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md) **[STARTER]**
+1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md) **(STARTER)**
1. You verify your changes with [JUnit test reports](../../../ci/junit_test_reports.md) in GitLab CI/CD
-1. You avoid using dependencies whose license is not compatible with your project with [License Management reports](license_management.md) **[ULTIMATE]**
+1. You avoid using dependencies whose license is not compatible with your project with [License Management reports](license_management.md) **(ULTIMATE)**
1. You request the [approval](#merge-request-approvals-starter) from your manager
1. Your manager pushes a commit with their final review, [approves the merge request](merge_request_approvals.md), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Starter)
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#whenmanual) for GitLab CI/CD
@@ -68,7 +68,7 @@ B. Consider you're a web developer writing a webpage for your company's website:
1. You gather feedback from your reviewers
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
1. You request your web designers for their implementation
-1. You request the [approval](merge_request_approvals.md) from your manager **[STARTER]**
+1. You request the [approval](merge_request_approvals.md) from your manager **(STARTER)**
1. Once approved, your merge request is [squashed and merged](squash_and_merge.md), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
@@ -172,7 +172,7 @@ in a Merge Request. To do so, click the **...** button in the gutter of the Merg
![Comment on any diff file line](img/comment-on-any-diff-line.png)
-## Perform a Review **[PREMIUM]**
+## Perform a Review **(PREMIUM)**
Start a review in order to create multiple comments on a diff and publish them once you're ready.
Starting a review allows you to get all your thoughts in order and ensure you haven't missed anything
@@ -197,7 +197,7 @@ can easily apply them to the codebase directly from the UI. Read
through the documentation on [Suggest changes](../../discussions/index.md#suggest-changes)
to learn more.
-## Multiple assignees **[STARTER]**
+## Multiple assignees **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/2004)
in [GitLab Starter 11.11](https://about.gitlab.com/pricing).
@@ -364,7 +364,7 @@ have been marked as a **Work In Progress**.
[Learn more about setting a merge request as "Work In Progress".](work_in_progress_merge_requests.md)
-## Merge request approvals **[STARTER]**
+## Merge request approvals **(STARTER)**
> Included in [GitLab Starter][products].
@@ -375,7 +375,7 @@ list of approvers that will need to approve every merge request in a project.
[Read more about merge request approvals.](merge_request_approvals.md)
-## Code Quality **[STARTER]**
+## Code Quality **(STARTER)**
> Introduced in [GitLab Starter][products] 9.3.
@@ -385,7 +385,7 @@ can show the Code Climate report right in the merge request widget area.
[Read more about Code Quality reports.](code_quality.md)
-## Browser Performance Testing **[PREMIUM]**
+## Browser Performance Testing **(PREMIUM)**
> Introduced in [GitLab Premium][products] 10.3.
@@ -395,7 +395,7 @@ GitLab runs the [Sitespeed.io container][sitespeed-container] and displays the d
[Read more about Browser Performance Testing.](browser_performance_testing.md)
-## Security reports **[ULTIMATE]**
+## Security reports **(ULTIMATE)**
GitLab can scan and report any vulnerabilities found in your project.
diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md
index 8e8ec26daf2..0f392676316 100644
--- a/doc/user/project/merge_requests/merge_request_approvals.md
+++ b/doc/user/project/merge_requests/merge_request_approvals.md
@@ -2,7 +2,7 @@
type: reference, concepts
---
-# Merge request approvals **[STARTER]**
+# Merge request approvals **(STARTER)**
> Introduced in [GitLab Enterprise Edition 7.12](https://about.gitlab.com/2015/06/22/gitlab-7-12-released/#merge-request-approvers-ee-only).
@@ -64,7 +64,7 @@ suitable to your workflow:
[overridden per merge request](#overriding-the-merge-request-approvals-default-settings)
- Choose whether [approvals will be reset with new pushed commits](#resetting-approvals-on-push)
-## Editing approvals **[PREMIUM]**
+## Editing approvals **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1979) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.
@@ -85,7 +85,7 @@ request approval rules:
![Approvals premium project edit](img/approvals_premium_project_edit.png)
-## Multiple approval rules **[PREMIUM]**
+## Multiple approval rules **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1979) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.
@@ -165,7 +165,7 @@ are other conditions that may block it, such as merge conflicts,
[pending discussions](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-discussions-are-resolved)
or a [failed CI/CD pipeline](merge_when_pipeline_succeeds.md).
-## Code Owners approvals **[PREMIUM]**
+## Code Owners approvals **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4418) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.9.
@@ -237,7 +237,7 @@ If you are contributing to a forked project, things are a little different.
Read what happens when the
[source and target branches are not the same](#merge-requests-with-different-source-branch-and-target-branch-projects).
-## Overriding merge request approvals default settings **[PREMIUM]**
+## Overriding merge request approvals default settings **(PREMIUM)**
In GitLab Premium, when the approval rules are [set at the project level](#editing-approvals-premium), and
**Can override approvers and approvals required per merge request** is checked, there are a few more
@@ -295,7 +295,7 @@ enabling [**Prevent approval of merge requests by their committers**](#prevent-a
1. Tick the checkbox **Prevent approval of merge requests by their committers**.
1. Click **Save changes**.
-## Require authentication when approving a merge request **[STARTER]**
+## Require authentication when approving a merge request **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5981) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0.
diff --git a/doc/user/project/milestones/burndown_charts.md b/doc/user/project/milestones/burndown_charts.md
index 7ffeb032d7f..84e08b4eeb8 100644
--- a/doc/user/project/milestones/burndown_charts.md
+++ b/doc/user/project/milestones/burndown_charts.md
@@ -1,4 +1,4 @@
-# Burndown Charts **[STARTER]**
+# Burndown Charts **(STARTER)**
> **Notes:**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1540) in [GitLab Starter](https://about.gitlab.com/pricing/) 9.1 for project milestones.
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 6cd866b5c0d..b0745b1e2c5 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -84,9 +84,9 @@ From the project issue/merge request list pages and the group issue/merge reques
### Filtering in issue boards
- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [search and filter bar](../../search/index.md#issue-boards).
-- From [group issue boards](../issue_board.md#group-issue-boards-premium), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **[PREMIUM]**
-- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[STARTER]**
-- From [group issue boards](../issue_board.md#group-issue-boards-premium) you can filter by only group milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **[STARTER]**
+- From [group issue boards](../issue_board.md#group-issue-boards-premium), you can filter by only group milestones in the [search and filter bar](../../search/index.md#issue-boards). **(PREMIUM)**
+- From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **(STARTER)**
+- From [group issue boards](../issue_board.md#group-issue-boards-premium) you can filter by only group milestones in the [issue board configuration](../issue_board.md#configurable-issue-boards-starter). **(STARTER)**
### Special milestone filters
@@ -124,13 +124,13 @@ These features are only available for project milestones and not group milestone
- Participants and labels that are used in issues and merge requests that have the milestone assigned are displayed.
- [Burndown chart](#project-burndown-charts-starter).
-### Project Burndown Charts **[STARTER]**
+### Project Burndown Charts **(STARTER)**
For project milestones in [GitLab Starter](https://about.gitlab.com/pricing), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone.
![burndown chart](img/burndown_chart.png)
-### Group Burndown Charts **[PREMIUM]**
+### Group Burndown Charts **(PREMIUM)**
For group milestones in [GitLab Premium](https://about.gitlab.com/pricing), a [burndown chart](burndown_charts.md) is in the milestone view, showing the progress of completing a milestone.
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index c07c4099f22..d35a8568394 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -185,7 +185,7 @@ The [Job environment variable][jobenv] `CI_JOB_TOKEN` can be used to
authenticate any clones of dependent repositories. For example:
```
-git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/myuser/mydependentrepo
+git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/<user>/<mydependentrepo>.git
```
It can also be used for system-wide authentication
diff --git a/doc/user/project/operations/feature_flags.md b/doc/user/project/operations/feature_flags.md
index a5db3d70bb9..fdc1e978291 100644
--- a/doc/user/project/operations/feature_flags.md
+++ b/doc/user/project/operations/feature_flags.md
@@ -1,4 +1,4 @@
-# Feature Flags **[PREMIUM]**
+# Feature Flags **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845) in GitLab 11.4.
diff --git a/doc/user/project/operations/index.md b/doc/user/project/operations/index.md
index 84711d1146f..2da9c3e70cf 100644
--- a/doc/user/project/operations/index.md
+++ b/doc/user/project/operations/index.md
@@ -7,6 +7,6 @@ your applications:
- Deploy to different [environments](../../../ci/environments.md).
- Connect your project to a [Kubernetes cluster](../clusters/index.md).
- Discover and view errors generated by your applications with [Error Tracking](error_tracking.md).
-- Create, toggle, and remove [Feature Flags](feature_flags.md). **[PREMIUM]**
-- [Trace](tracing.md) the performance and health of a deployed application. **[ULTIMATE]**
+- Create, toggle, and remove [Feature Flags](feature_flags.md). **(PREMIUM)**
+- [Trace](tracing.md) the performance and health of a deployed application. **(ULTIMATE)**
- Add a [button to the Monitoring dashboard](linking_to_an_external_dashboard.md) linking directly to your existing external dashboards.
diff --git a/doc/user/project/operations/tracing.md b/doc/user/project/operations/tracing.md
index 0416e096cf2..b92d2e49839 100644
--- a/doc/user/project/operations/tracing.md
+++ b/doc/user/project/operations/tracing.md
@@ -1,4 +1,4 @@
-# Tracing **[ULTIMATE]**
+# Tracing **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/7903) in GitLab Ultimate 11.5.
@@ -17,8 +17,8 @@ systems.
### Deploying Jaeger
To learn more about deploying Jaeger, read the official
-[Getting Started documentation](https://www.jaegertracing.io/docs/latest/getting-started/).
-There is an easy to use [all-in-one Docker image](https://www.jaegertracing.io/docs/latest/getting-started/#AllinoneDockerimage),
+[Getting Started documentation](https://www.jaegertracing.io/docs/1.13/getting-started/).
+There is an easy to use [all-in-one Docker image](https://www.jaegertracing.io/docs/1.13/getting-started/#AllinoneDockerimage),
as well as deployment options for [Kubernetes](https://github.com/jaegertracing/jaeger-kubernetes)
and [OpenShift](https://github.com/jaegertracing/jaeger-openshift).
@@ -27,8 +27,8 @@ and [OpenShift](https://github.com/jaegertracing/jaeger-openshift).
GitLab provides an easy way to open the Jaeger UI from within your project:
1. [Set up Jaeger](#deploying-jaeger) and configure your application using one of the
- [client libraries](https://www.jaegertracing.io/docs/latest/client-libraries/).
+ [client libraries](https://www.jaegertracing.io/docs/1.13/client-libraries/).
1. Navigate to your project's **Settings > Operations** and provide the Jaeger URL.
1. Click **Save changes** for the changes to take effect.
1. You can now visit **Operations > Tracing** in your project's sidebar and
- GitLab will redirect you to the configured Jaeger URL. \ No newline at end of file
+ GitLab will redirect you to the configured Jaeger URL.
diff --git a/doc/user/project/packages/maven_repository.md b/doc/user/project/packages/maven_repository.md
index 9b7af738696..27c052fb2bc 100644
--- a/doc/user/project/packages/maven_repository.md
+++ b/doc/user/project/packages/maven_repository.md
@@ -1,4 +1,4 @@
-# GitLab Maven Repository **[PREMIUM]**
+# GitLab Maven Repository **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5811) in
[GitLab Premium](https://about.gitlab.com/pricing/) 11.3.
@@ -12,7 +12,7 @@ project can have its own space to store its Maven artifacts.
NOTE: **Note:**
This option is available only if your GitLab administrator has
-[enabled support for the Maven repository](../../../administration/packages.md).**[PREMIUM ONLY]**
+[enabled support for the Maven repository](../../../administration/packages.md).**(PREMIUM ONLY)**
After the Packages feature is enabled, the Maven Repository will be available for
all new projects by default. To enable it for existing projects, or if you want
diff --git a/doc/user/project/packages/npm_registry.md b/doc/user/project/packages/npm_registry.md
index 2e274573434..481b1ce0337 100644
--- a/doc/user/project/packages/npm_registry.md
+++ b/doc/user/project/packages/npm_registry.md
@@ -1,4 +1,4 @@
-# GitLab NPM Registry **[PREMIUM]**
+# GitLab NPM Registry **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5934)
in [GitLab Premium](https://about.gitlab.com/pricing/) 11.7.
@@ -11,16 +11,11 @@ project can have its own space to store NPM packages.
NOTE: **Note:**
Only [scoped](https://docs.npmjs.com/misc/scope) packages are supported.
-
-NOTE: **Note:**
-As `@group/subgroup/project` is not a valid NPM package name, publishing a package
-within a subgroup is not supported yet.
-
## Enabling the NPM Registry
NOTE: **Note:**
This option is available only if your GitLab administrator has
-[enabled support for the NPM registry](../../../administration/packages.md).**[PREMIUM ONLY]**
+[enabled support for the NPM registry](../../../administration/packages.md).**(PREMIUM ONLY)**
After the NPM registry is enabled, it will be available for all new projects
by default. To enable it for existing projects, or if you want to disable it:
@@ -36,12 +31,15 @@ get familiar with the package naming convention.
## Package naming convention
-**Only packages that have the same path as the project** are supported. For
- example:
+**Packages must be scoped in the root namespace of the project**. The package
+name may be anything but it is preferred that the project name be used unless
+it is not possible due to a naming collision. For example:
| Project | Package | Supported |
| ---------------------- | ----------------------- | --------- |
| `foo/bar` | `@foo/bar` | Yes |
+| `foo/bar/baz` | `@foo/baz` | Yes |
+| `foo/bar/buz` | `@foo/anything` | Yes |
| `gitlab-org/gitlab-ce` | `@gitlab-org/gitlab-ce` | Yes |
| `gitlab-org/gitlab-ce` | `@foo/bar` | No |
@@ -113,6 +111,9 @@ npm publish
You can then navigate to your project's **Packages** page and see the uploaded
packages or even delete them.
+If you attempt to publish a package with a name that already exists within
+a given scope, you will receive a `403 Forbidden!` error.
+
## Uploading a package with the same version twice
If you upload a package with a same name and version twice, GitLab will show
diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md
index 8baf41dba78..d844d4222b1 100644
--- a/doc/user/project/pages/getting_started_part_four.md
+++ b/doc/user/project/pages/getting_started_part_four.md
@@ -380,7 +380,7 @@ What you can do with GitLab CI is pretty much up to your
creativity. Once you get used to it, you start creating
awesome scripts that automate most of tasks you'd do
manually in the past. Read through the
-[documentation of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
+[documentation of GitLab CI](../../../ci/yaml/README.md)
to understand how to go even further on your scripts.
- On this blog post, understand the concept of
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index fa79c393b72..64b1e259292 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -140,7 +140,7 @@ To learn more about configuration options for GitLab Pages, read the following:
| [Static websites and Pages domains](getting_started_part_one.md) | Understand what is a static website, and how GitLab Pages default domains work. |
| [Projects and URL structure](getting_started_part_two.md) | Forking projects and creating new ones from scratch, understanding URLs structure and baseurls. |
| [GitLab CI/CD for GitLab Pages](getting_started_part_four.md) | Understand how to create your own `.gitlab-ci.yml` for your site. |
-| [Exploring GitLab Pages](introduction.md) | Requirements, technical aspects, specific GitLab CI's configuration options, custom 404 pages, limitations, FAQ. |
+| [Exploring GitLab Pages](introduction.md) | Requirements, technical aspects, specific GitLab CI's configuration options, Access Control, custom 404 pages, limitations, FAQ. |
|---+---|
| [Custom domains and SSL/TLS Certificates](getting_started_part_three.md) | How to add custom domains and subdomains to your website, configure DNS records and SSL/TLS certificates. |
| [CloudFlare certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Secure your Pages site with CloudFlare certificates. |
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index 4ea3bd9be9b..9451b5349c0 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -67,7 +67,7 @@ Some static site generators provide plugins for that functionality so that you
don't have to create and edit HTML files manually. For example, Jekyll has the
[redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from).
-## GitLab Pages Access Control **[CORE ONLY]**
+## GitLab Pages Access Control **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) in GitLab 11.5.
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index f9feae36dbc..1966b136e9d 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -187,10 +187,13 @@ information in the UI.
DANGER: **Warning:**
This is a destructive action that leads to data loss. Use with caution.
-If you are either the owner of a given job or have Master
-[permissions](../../permissions.md#gitlab-cicd-permissions)
-on the project, you can erase a single job via the UI which will also remove the
-artifacts and the job's trace.
+You can erase a single job via the UI, which will also remove the job's
+artifacts and trace, if you are:
+
+- The owner of the job.
+- A [Maintainer](../../permissions.md#gitlab-cicd-permissions) of the project.
+
+To erase a job:
1. Navigate to a job's page.
1. Click the trash icon at the top right of the job's trace.
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 99cede557a0..20a03dff2da 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -66,7 +66,7 @@ dropdown list in the "Already protected" area.
If you don't choose any of those options while creating a protected branch,
they are set to "Maintainers" by default.
-## Restricting push and merge access to certain users **[STARTER]**
+## Restricting push and merge access to certain users **(STARTER)**
> This feature was [introduced][ce-5081] in [GitLab Starter][ee] 8.11.
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 1281ba561b8..8b8aa51b6dd 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -16,8 +16,8 @@ discussions, and descriptions:
|:---------------------------|:------------------------------ |:------|:--------------|
| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ ┻━┻` | ✓ | ✓ |
| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` | ✓ | ✓ |
-| `/todo` | Add a todo | ✓ | ✓ |
-| `/done` | Mark todo as done | ✓ | ✓ |
+| `/todo` | Add a To Do | ✓ | ✓ |
+| `/done` | Mark To Do as done | ✓ | ✓ |
| `/subscribe` | Subscribe | ✓ | ✓ |
| `/unsubscribe` | Unsubscribe | ✓ | ✓ |
| `/close` | Close | ✓ | ✓ |
@@ -26,9 +26,9 @@ discussions, and descriptions:
| `/award :emoji:` | Toggle emoji award | ✓ | ✓ |
| `/assign me` | Assign yourself | ✓ | ✓ |
| `/assign @user` | Assign one user | ✓ | ✓ |
-| `/assign @user1 @user2` | Assign multiple users **[STARTER]** | ✓ | ✓ |
-| `/unassign @user1 @user2` | Remove assignee(s) **[STARTER]** | ✓ | ✓ |
-| `/reassign @user1 @user2` | Change assignee **[STARTER]** | ✓ | ✓ |
+| `/assign @user1 @user2` | Assign multiple users **(STARTER)** | ✓ | ✓ |
+| `/unassign @user1 @user2` | Remove assignee(s) **(STARTER)** | ✓ | ✓ |
+| `/reassign @user1 @user2` | Change assignee **(STARTER)** | ✓ | ✓ |
| `/unassign` | Remove current assignee | ✓ | ✓ |
| `/milestone %milestone` | Set milestone | ✓ | ✓ |
| `/remove_milestone` | Remove milestone | ✓ | ✓ |
@@ -44,11 +44,11 @@ discussions, and descriptions:
| `/unlock` | Unlock the discussion | ✓ | ✓ |
| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code>| Set due date | ✓ | |
| `/remove_due_date` | Remove due date | ✓ | |
-| <code>/weight &lt;0 &#124; 1 &#124; 2 &#124; ...&gt;</code> | Set weight **[STARTER]** | ✓ | |
-| `/clear_weight` | Clears weight **[STARTER]** | ✓ | |
-| <code>/epic &lt;&epic &#124; group&epic &#124; Epic URL&gt;</code> | Add to epic **[ULTIMATE]** | ✓ | |
-| `/remove_epic` | Removes from epic **[ULTIMATE]** | ✓ | |
-| `/promote` | Promote issue to epic **[ULTIMATE]** | ✓ | |
+| <code>/weight &lt;0 &#124; 1 &#124; 2 &#124; ...&gt;</code> | Set weight **(STARTER)** | ✓ | |
+| `/clear_weight` | Clears weight **(STARTER)** | ✓ | |
+| <code>/epic &lt;&epic &#124; group&epic &#124; Epic URL&gt;</code> | Add to epic **(ULTIMATE)** | ✓ | |
+| `/remove_epic` | Removes from epic **(ULTIMATE)** | ✓ | |
+| `/promote` | Promote issue to epic **(ULTIMATE)** | ✓ | |
| `/confidential` | Make confidential | ✓ | |
| `/duplicate <#issue>` | Mark this issue as a duplicate of another issue | ✓ |
| `/move <path/to/project>` | Move this issue to another project | ✓ | |
@@ -57,7 +57,7 @@ discussions, and descriptions:
| `/approve` | Approve the merge request | | ✓ |
| `/merge` | Merge (when pipeline succeeds) | | ✓ |
| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
-| `/relate #issue1 #issue2` | Mark issues as related **[STARTER]** | ✓ | |
+| `/relate #issue1 #issue2` | Mark issues as related **(STARTER)** | ✓ | |
## Quick actions for commit messages
@@ -67,7 +67,7 @@ The following quick actions are applicable for commit messages:
|:------------------------|:------------------------------------------|
| `/tag v1.2.3 <message>` | Tags this commit with an optional message |
-## Quick actions for Epics **[ULTIMATE]**
+## Quick actions for Epics **(ULTIMATE)**
The following quick actions are applicable for epics threads and description:
@@ -75,8 +75,8 @@ The following quick actions are applicable for epics threads and description:
|:---------------------------|:----------------------------------------|
| `/tableflip <Comment>` | Append the comment with `(╯°□°)╯︵ ┻━┻` |
| `/shrug <Comment>` | Append the comment with `¯\_(ツ)_/¯` |
-| `/todo` | Add a todo |
-| `/done` | Mark todo as done |
+| `/todo` | Add a To Do |
+| `/done` | Mark To Do as done |
| `/subscribe` | Subscribe |
| `/unsubscribe` | Unsubscribe |
| `/close` | Close |
@@ -88,3 +88,5 @@ The following quick actions are applicable for epics threads and description:
| `/relabel ~label1 ~label2` | Replace label |
| <code>/child_epic &lt;&epic &#124; group&epic &#124; Epic URL&gt;</code> | Adds child epic to epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) |
| <code>/remove_child_epic &lt;&epic &#124; group&epic &#124; Epic URL&gt;</code> | Removes child epic from epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-ee/issues/7330)) |
+| <code>/parent_epic &lt;&epic &#124; group&epic &#124; Epic URL&gt;</code> | Sets parent epic to epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/10556)) |
+| <code>/remove_parent_epic | Removes parent epic from epic ([introduced in GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/10556)) |
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 13e4f2ce163..a81c9197ec1 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -21,7 +21,7 @@ branch for your project. You can choose another branch to be your project's
default under your project's **Settings > Repository**.
The default branch is the branch affected by the
-[issue closing pattern](../../issues/automatic_issue_closing.md),
+[issue closing pattern](../../issues/managing_issues.md#closing-issues-automatically),
which means that _an issue will be closed when a merge request is merged to
the **default branch**_.
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index 3260a355fdc..cf0a986887e 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -261,7 +261,7 @@ To remove a GPG key from your account:
1. Navigate to the **GPG keys** tab.
1. Click on the trash icon besides the GPG key you want to delete.
-## Rejecting commits that are not signed **[PREMIUM]**
+## Rejecting commits that are not signed **(PREMIUM)**
You can configure your project to reject commits that aren't GPG-signed
via [push rules](../../../../push_rules/push_rules.md).
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 165f4c15165..5b5685b3418 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -171,17 +171,14 @@ Via command line, you can commit multiple times before pushing.
- **Sign a commit:**
Use GPG to [sign your commits](gpg_signed_commits/index.md).
-## Repository size
+## Project and repository size
-A project's repository size is reported on the project's **Details** page. The reported size is
-updated every 15 minutes at most, so may not reflect recent activity.
+A project's size is reported on the project's **Details** page. The reported size is
+updated every 15 minutes at most, so may not reflect recent activity. The displayed files size includes repository files, artifacts, and LFS.
-The repository size for:
+The project size may differ slightly from one instance to another due to compression, housekeeping, and other factors.
-- GitLab.com [is set by GitLab](../../gitlab_com/index.md#repository-size-limit).
-- Self-managed instances is set by your GitLab administrators.
-
-You can [reduce a repository's size using Git](reducing_the_repo_size_using_git.md).
+[Repository size limit](../../admin_area/settings/account_and_limit_settings.md) may be set by admins. GitLab.com's repository size limit [is set by GitLab](../../gitlab_com/index.md#repository-size-limit).
## Contributors
@@ -224,7 +221,7 @@ Select branches to compare using the [branch filter search box](branches/index.m
Find it under your project's **Repository > Compare**.
-## Locked files **[PREMIUM]**
+## Locked files **(PREMIUM)**
Use [File Locking](../file_lock.md) to
lock your files to prevent any conflicting changes.
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index ce9d23bf911..253e5374f52 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -114,7 +114,7 @@ If your [project is already configured with a deployment service][project-servic
After the branch is created, you can edit files in the repository to fix
the issue. When a merge request is created based on the newly created branch,
-the description field will automatically display the [issue closing pattern]
+the description field will automatically display the [issue closing pattern](../issues/managing_issues.md#closing-issues-automatically)
`Closes #ID`, where `ID` the ID of the issue. This will close the issue once the
merge request is merged.
@@ -181,4 +181,3 @@ through the web editor, you can choose to use another of your linked email
addresses from the **User Settings > Edit Profile** page.
[ce-2808]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2808
-[issue closing pattern]: ../issues/automatic_issue_closing.md
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index 1a582164d03..f899120f0c2 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -1,4 +1,4 @@
-# Service Desk **[PREMIUM]**
+# Service Desk **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/149) in [GitLab Premium 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#service-desk-eep).
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 819515d7a4c..98bcc7cc09f 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -74,6 +74,13 @@ The following items will NOT be exported:
- CI variables
- Webhooks
- Any encrypted tokens
+- Merge Request Approvers
+- Push Rules
+- Awards
+
+NOTE: **Note:**
+For more details on the specific data persisted in a project export, see the
+[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/lib/gitlab/import_export/import_export.yml) file.
## Exporting a project and its data
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 2bf8d4dfe7b..06b431decad 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -9,7 +9,7 @@ to your project's homepage and clicking **Settings**.
## General settings
-Under a project's general settings you can find everything concerning the
+Under a project's general settings, you can find everything concerning the
functionality of a project.
### General project settings
@@ -26,7 +26,7 @@ Set up your project's access, [visibility](../../../public_access/public_access.
![projects sharing permissions](img/sharing_and_permissions_settings.png)
-If Issues are disabled, or you can't access Issues because you're not a project member, then Lables and Milestones
+If Issues are disabled, or you can't access Issues because you're not a project member, then Labels and Milestones
links will be missing from the sidebar UI.
You can still access them with direct links if you can access Merge Requests. This is deliberate, if you can see
@@ -42,13 +42,13 @@ Set up your project's merge request settings:
- Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.html)).
- Merge request [description templates](../description_templates.md#description-templates).
-- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **[STARTER]**
+- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **(STARTER)**
- Enable [merge only of pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md).
- Enable [merge only when all discussions are resolved](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-discussions-are-resolved).
![project's merge request settings](img/merge_requests_settings.png)
-### Service Desk **[PREMIUM]**
+### Service Desk **(PREMIUM)**
Enable [Service Desk](../service_desk.md) for your project to offer customer support.
@@ -96,7 +96,7 @@ To rename a repository:
1. Hit **Rename project**.
Remember that this can have unintended side effects since everyone with the
-old URL will not be able to push or pull. Read more about what happens with the
+old URL will not be able to push or pull. Read more about what happens with the
[redirects when renaming repositories](../index.md#redirects-when-changing-repository-paths).
#### Transferring an existing project into another namespace
@@ -135,6 +135,6 @@ namespace if needed.
Configure Error Tracking to discover and view [Sentry errors within GitLab](../operations/error_tracking.md).
-### Jaeger tracing **[ULTIMATE]**
+### Jaeger tracing **(ULTIMATE)**
Add the URL of a Jaeger server to allow your users to [easily access the Jaeger UI from within GitLab](../operations/tracing.md).
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 7d85f4adfed..3d92508ad04 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -129,7 +129,7 @@ below.
}
```
-## Interactive Web Terminals for the Web IDE **[ULTIMATE ONLY]**
+## Interactive Web Terminals for the Web IDE **(ULTIMATE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5426) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.6.
@@ -151,7 +151,7 @@ to work:
- The Runner needs to have
[`[session_server]` configured properly](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section).
- If you are using a reverse proxy with your GitLab instance, web terminals need to be
- [enabled](../../../administration/integration/terminal.md#enabling-and-disabling-terminal-support). **[ULTIMATE ONLY]**
+ [enabled](../../../administration/integration/terminal.md#enabling-and-disabling-terminal-support). **(ULTIMATE ONLY)**
If you have the terminal open and the job has finished with its tasks, the
terminal will block the job from finishing for the duration configured in
diff --git a/doc/user/search/advanced_global_search.md b/doc/user/search/advanced_global_search.md
index f80f4183802..52da6d65000 100644
--- a/doc/user/search/advanced_global_search.md
+++ b/doc/user/search/advanced_global_search.md
@@ -1,4 +1,4 @@
-# Advanced Global Search **[STARTER ONLY]**
+# Advanced Global Search **(STARTER ONLY)**
> - [Introduced][ee-109] in GitLab [Starter][ee] 8.4.
> - This is the user documentation. To install and configure Elasticsearch,
diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md
index d302cb7a809..ad9f065b19b 100644
--- a/doc/user/search/advanced_search_syntax.md
+++ b/doc/user/search/advanced_search_syntax.md
@@ -1,4 +1,4 @@
-# Advanced Syntax Search **[STARTER ONLY]**
+# Advanced Syntax Search **(STARTER ONLY)**
> **Notes:**
> - Introduced in [GitLab Enterprise Starter][ee] 9.2
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index bb6c48471c7..c34b9ae3d7e 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -97,10 +97,10 @@ quickly access issues and merge requests created or assigned to you within that
![search per project - shortcut](img/project_search.png)
-## Todos
+## To-Do List
-Your [todos](../../workflow/todos.md#gitlab-todos) can be searched by "to do" and "done".
-You can [filter](../../workflow/todos.md#filtering-your-todos) them per project,
+Your [To-Do List](../../workflow/todos.md#gitlab-to-do-list) can be searched by "to do" and "done".
+You can [filter](../../workflow/todos.md#filtering-your-to-do-list) them per project,
author, type, and action. Also, you can sort them by
[**Label priority**](../../user/project/labels.md#label-priority),
**Last created** and **Oldest created**.
@@ -143,14 +143,14 @@ and **Labels**, select multiple issues to add to a list of your choice:
![search and select issues to add to board](img/search_issues_board.png)
-## Advanced Global Search **[STARTER]**
+## Advanced Global Search **(STARTER)**
Leverage Elasticsearch for faster, more advanced code search across your entire
GitLab instance.
[Learn how to use the Advanced Global Search.](advanced_global_search.md)
-## Advanced Syntax Search **[STARTER]**
+## Advanced Syntax Search **(STARTER)**
Use advanced queries for more targeted search results.
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 40e2486ace5..6ad61932868 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -4,7 +4,7 @@ comments: false
# Workflow
-- [Automatic issue closing](../user/project/issues/automatic_issue_closing.md)
+- [Automatic issue closing](../user/project/issues/managing_issues.md#closing-issues-automatically)
- [Change your time zone](timezone.md)
- [Cycle Analytics](../user/project/cycle_analytics.md)
- [Description templates](../user/project/description_templates.md)
@@ -13,15 +13,15 @@ comments: false
- [Groups](../user/group/index.md)
- Issues - The GitLab Issue Tracker is an advanced and complete tool for
tracking the evolution of a new idea or the process of solving a problem.
- - [Exporting Issues](../user/project/issues/csv_export.md) **[STARTER]** Export issues as a CSV, emailed as an attachment.
+ - [Exporting Issues](../user/project/issues/csv_export.md) **(STARTER)** Export issues as a CSV, emailed as an attachment.
- [Confidential issues](../user/project/issues/confidential_issues.md)
- [Due date for issues](../user/project/issues/due_dates.md)
- [Issue Board](../user/project/issue_board.md)
- [Keyboard shortcuts](shortcuts.md)
- [File finder](file_finder.md)
-- [File lock](../user/project/file_lock.md) **[PREMIUM]**
+- [File lock](../user/project/file_lock.md) **(PREMIUM)**
- [Labels](../user/project/labels.md)
-- [Issue weight](issue_weight.md) **[STARTER]**
+- [Issue weight](issue_weight.md) **(STARTER)**
- [Notification emails](notifications.md)
- [Projects](../user/project/index.md)
- [Project forking workflow](forking_workflow.md)
@@ -44,9 +44,9 @@ comments: false
- [Merge requests versions](../user/project/merge_requests/versions.md)
- ["Work In Progress" merge requests](../user/project/merge_requests/work_in_progress_merge_requests.md)
- [Fast-forward merge requests](../user/project/merge_requests/fast_forward_merge.md)
- - [Merge request approvals](../user/project/merge_requests/merge_request_approvals.md) **[STARTER]**
-- [Repository mirroring](repository_mirroring.md) **[STARTER]**
-- [Service Desk](../user/project/service_desk.md) **[PREMIUM]**
+ - [Merge request approvals](../user/project/merge_requests/merge_request_approvals.md) **(STARTER)**
+- [Repository mirroring](repository_mirroring.md) **(STARTER)**
+- [Service Desk](../user/project/service_desk.md) **(PREMIUM)**
- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md)
- [Importing from SVN, GitHub, Bitbucket, etc](importing/README.md)
- [Todos](todos.md)
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index 0cb390e1242..2f365e42cc9 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -1,7 +1,8 @@
-![GitLab Flow](gitlab_flow.png)
# Introduction to GitLab Flow
+![GitLab Flow](img/gitlab_flow.png)
+
Git allows a wide variety of branching strategies and workflows.
Because of this, many organizations end up with workflows that are too complicated, not clearly defined, or not integrated with issue tracking systems.
Therefore, we propose GitLab flow as a clearly defined set of best practices.
@@ -11,7 +12,7 @@ Organizations coming to Git from other version control systems frequently find i
This article describes GitLab flow, which integrates the Git workflow with an issue tracking system.
It offers a simple, transparent, and effective way to work with Git.
-![Four stages (working copy, index, local repo, remote repo) and three steps between them](four_stages.png)
+![Four stages (working copy, index, local repo, remote repo) and three steps between them](img/four_stages.png)
When converting to Git, you have to get used to the fact that it takes three steps to share a commit with colleagues.
Most version control systems have only one step: committing from the working copy to a shared server.
@@ -19,7 +20,7 @@ In Git, you add files from the working copy to the staging area. After that, you
The third step is pushing to a shared remote repository.
After getting used to these three steps, the next challenge is the branching model.
-![Multiple long-running branches and merging in all directions](messy_flow.png)
+![Multiple long-running branches and merging in all directions](img/messy_flow.png)
Since many organizations new to Git have no conventions for how to work with it, their repositories can quickly become messy.
The biggest problem is that many long-running branches emerge that all contain part of the changes.
@@ -31,7 +32,7 @@ For a video introduction of how this works in GitLab, see [GitLab Flow](https://
## Git flow and its problems
-![Git Flow timeline by Vincent Driessen, used with permission](gitdashflow.png)
+![Git Flow timeline by Vincent Driessen, used with permission](img/gitdashflow.png)
Git flow was one of the first proposals to use Git branches, and it has received a lot of attention.
It suggests a `master` branch and a separate `develop` branch, as well as supporting branches for features, releases, and hotfixes.
@@ -54,7 +55,7 @@ For example, many projects do releases but don't need to do hotfixes.
## GitHub flow as a simpler alternative
-![Master branch with feature branches merged in](github_flow.png)
+![Master branch with feature branches merged in](img/github_flow.png)
In reaction to Git flow, GitHub created a simpler alternative.
[GitHub flow](https://guides.github.com/introduction/flow/index.html) has only feature branches and a `master` branch.
@@ -66,7 +67,7 @@ With GitLab flow, we offer additional guidance for these questions.
## Production branch with GitLab flow
-![Master branch and production branch with an arrow that indicates a deployment](production_branch.png)
+![Master branch and production branch with an arrow that indicates a deployment](img/production_branch.png)
GitHub flow assumes you can deploy to production every time you merge a feature branch.
While this is possible in some cases, such as SaaS applications, there are many cases where this is not possible.
@@ -82,7 +83,7 @@ This flow prevents the overhead of releasing, tagging, and merging that happens
## Environment branches with GitLab flow
-![Multiple branches with the code cascading from one to another](environment_branches.png)
+![Multiple branches with the code cascading from one to another](img/environment_branches.png)
It might be a good idea to have an environment that is automatically updated to the `master` branch.
Only, in this case, the name of this environment might differ from the branch name.
@@ -98,7 +99,7 @@ If this is not possible because more manual testing is required, you can send me
## Release branches with GitLab flow
-![Master and multiple release branches that vary in length with cherry-picks from master](release_branches.png)
+![Master and multiple release branches that vary in length with cherry-picks from master](img/release_branches.png)
You only need to work with release branches if you need to release software to the outside world.
In this case, each branch contains a minor version, for example, 2-3-stable, 2-4-stable, etc.
@@ -114,7 +115,7 @@ In this flow, it is not common to have a production branch (or Git flow `master`
## Merge/pull requests with GitLab flow
-![Merge request with inline comments](mr_inline_comments.png)
+![Merge request with inline comments](img/mr_inline_comments.png)
Merge or pull requests are created in a Git management application. They ask an assigned person to merge two branches.
Tools such as GitHub and Bitbucket choose the name "pull request" since the first manual action is to pull the feature branch.
@@ -147,11 +148,11 @@ It also ensures that if someone reopens the issue, they can use the same branch
NOTE: **Note:**
When you reopen an issue you need to create a new merge request.
-![Remove checkbox for branch in merge requests](remove_checkbox.png)
+![Remove checkbox for branch in merge requests](img/remove_checkbox.png)
## Issue tracking with GitLab flow
-![Merge request with the branch name "15-require-a-password-to-change-it" and assignee field shown](merge_request.png)
+![Merge request with the branch name "15-require-a-password-to-change-it" and assignee field shown](img/merge_request.png)
GitLab flow is a way to make the relation between the code and the issue tracker more transparent.
@@ -192,7 +193,7 @@ It is possible that one feature branch solves more than one issue.
## Linking and closing issues from merge requests
-![Merge request showing the linked issues that will be closed](close_issue_mr.png)
+![Merge request showing the linked issues that will be closed](img/close_issue_mr.png)
Link to issues by mentioning them in commit messages or the description of a merge request, for example, "Fixes #16" or "Duck typing is preferred. See #12."
GitLab then creates links to the mentioned issues and creates comments in the issues linking back to the merge request.
@@ -203,7 +204,7 @@ If you have an issue that spans across multiple repositories, create an issue fo
## Squashing commits with rebase
-![Vim screen showing the rebase view](rebase.png)
+![Vim screen showing the rebase view](img/rebase.png)
With Git, you can use an interactive rebase (`rebase -i`) to squash multiple commits into one or reorder them.
This functionality is useful if you want to replace a couple of small commits with a single commit, or if you want to make the order more logical.
@@ -229,7 +230,7 @@ Git does not allow you to merge the code again otherwise.
## Reducing merge commits in feature branches
-![List of sequential merge commits](merge_commits.png)
+![List of sequential merge commits](img/merge_commits.png)
Having lots of merge commits can make your repository history messy.
Therefore, you should try to avoid merge commits in feature branches.
@@ -289,7 +290,7 @@ Sharing your work before it's complete also allows for discussion and feedback a
## How to write a good commit message
-![Good and bad commit message](good_commit.png)
+![Good and bad commit message](img/good_commit.png)
A commit message should reflect your intention, not just the contents of the commit.
It is easy to see the changes in a commit, so the commit message should explain why you made those changes.
@@ -300,7 +301,7 @@ For more information about formatting commit messages, please see this excellent
## Testing before merging
-![Merge requests showing the test states: red, yellow, and green](ci_mr.png)
+![Merge requests showing the test states: red, yellow, and green](img/ci_mr.png)
In old workflows, the continuous integration (CI) server commonly ran tests on the `master` branch only.
Developers had to ensure their code did not break the `master` branch.
@@ -317,7 +318,7 @@ As said before, if you often have feature branches that last for more than a few
## Working with feature branches
-![Shell output showing git pull output](git_pull.png)
+![Shell output showing git pull output](img/git_pull.png)
When creating a feature branch, always branch from an up-to-date `master`.
If you know before you start that your work depends on another branch, you can also branch from there.
diff --git a/doc/workflow/ci_mr.png b/doc/workflow/img/ci_mr.png
index 85a609cb814..85a609cb814 100644
--- a/doc/workflow/ci_mr.png
+++ b/doc/workflow/img/ci_mr.png
Binary files differ
diff --git a/doc/workflow/close_issue_mr.png b/doc/workflow/img/close_issue_mr.png
index 70de2fb6cee..70de2fb6cee 100644
--- a/doc/workflow/close_issue_mr.png
+++ b/doc/workflow/img/close_issue_mr.png
Binary files differ
diff --git a/doc/workflow/environment_branches.png b/doc/workflow/img/environment_branches.png
index 0aff33c6bb8..0aff33c6bb8 100644
--- a/doc/workflow/environment_branches.png
+++ b/doc/workflow/img/environment_branches.png
Binary files differ
diff --git a/doc/workflow/four_stages.png b/doc/workflow/img/four_stages.png
index 3ef6a33d2d4..3ef6a33d2d4 100644
--- a/doc/workflow/four_stages.png
+++ b/doc/workflow/img/four_stages.png
Binary files differ
diff --git a/doc/workflow/git_pull.png b/doc/workflow/img/git_pull.png
index 0e56e59471c..0e56e59471c 100644
--- a/doc/workflow/git_pull.png
+++ b/doc/workflow/img/git_pull.png
Binary files differ
diff --git a/doc/workflow/gitdashflow.png b/doc/workflow/img/gitdashflow.png
index 65900853d84..65900853d84 100644
--- a/doc/workflow/gitdashflow.png
+++ b/doc/workflow/img/gitdashflow.png
Binary files differ
diff --git a/doc/workflow/github_flow.png b/doc/workflow/img/github_flow.png
index 21a22becdb6..21a22becdb6 100644
--- a/doc/workflow/github_flow.png
+++ b/doc/workflow/img/github_flow.png
Binary files differ
diff --git a/doc/workflow/gitlab_flow.png b/doc/workflow/img/gitlab_flow.png
index a6f3c947843..a6f3c947843 100644
--- a/doc/workflow/gitlab_flow.png
+++ b/doc/workflow/img/gitlab_flow.png
Binary files differ
diff --git a/doc/workflow/good_commit.png b/doc/workflow/img/good_commit.png
index ceb0d4b1691..ceb0d4b1691 100644
--- a/doc/workflow/good_commit.png
+++ b/doc/workflow/img/good_commit.png
Binary files differ
diff --git a/doc/workflow/merge_commits.png b/doc/workflow/img/merge_commits.png
index 4a80811c6e3..4a80811c6e3 100644
--- a/doc/workflow/merge_commits.png
+++ b/doc/workflow/img/merge_commits.png
Binary files differ
diff --git a/doc/workflow/merge_request.png b/doc/workflow/img/merge_request.png
index 010e95983fc..010e95983fc 100644
--- a/doc/workflow/merge_request.png
+++ b/doc/workflow/img/merge_request.png
Binary files differ
diff --git a/doc/workflow/messy_flow.png b/doc/workflow/img/messy_flow.png
index 4fa22d2bb5d..4fa22d2bb5d 100644
--- a/doc/workflow/messy_flow.png
+++ b/doc/workflow/img/messy_flow.png
Binary files differ
diff --git a/doc/workflow/mr_inline_comments.png b/doc/workflow/img/mr_inline_comments.png
index a18801f56e4..a18801f56e4 100644
--- a/doc/workflow/mr_inline_comments.png
+++ b/doc/workflow/img/mr_inline_comments.png
Binary files differ
diff --git a/doc/workflow/production_branch.png b/doc/workflow/img/production_branch.png
index c132d51bfb6..c132d51bfb6 100644
--- a/doc/workflow/production_branch.png
+++ b/doc/workflow/img/production_branch.png
Binary files differ
diff --git a/doc/workflow/rebase.png b/doc/workflow/img/rebase.png
index fe865177ba8..fe865177ba8 100644
--- a/doc/workflow/rebase.png
+++ b/doc/workflow/img/rebase.png
Binary files differ
diff --git a/doc/workflow/release_branches.png b/doc/workflow/img/release_branches.png
index 0a7f61d0248..0a7f61d0248 100644
--- a/doc/workflow/release_branches.png
+++ b/doc/workflow/img/release_branches.png
Binary files differ
diff --git a/doc/workflow/remove_checkbox.png b/doc/workflow/img/remove_checkbox.png
index fb0e792b37b..fb0e792b37b 100644
--- a/doc/workflow/remove_checkbox.png
+++ b/doc/workflow/img/remove_checkbox.png
Binary files differ
diff --git a/doc/workflow/issue_weight.md b/doc/workflow/issue_weight.md
index 267160dae2a..afb623e1967 100644
--- a/doc/workflow/issue_weight.md
+++ b/doc/workflow/issue_weight.md
@@ -1,4 +1,4 @@
-# Issue Weight **[STARTER]**
+# Issue Weight **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/76)
> in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 6d941135bf2..03af8fad759 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -54,7 +54,7 @@ to offload local hard disk R/W operations, and free up disk space significantly.
GitLab is tightly integrated with `Fog`, so you can refer to its [documentation](http://fog.io/about/provider_documentation.html)
to check which storage services can be integrated with GitLab.
You can also use external object storage in a private local network. For example,
-[Minio](https://www.minio.io/) is a standalone object storage service, is easy to set up, and works well with GitLab instances.
+[Minio](https://min.io/) is a standalone object storage service, is easy to set up, and works well with GitLab instances.
GitLab provides two different options for the uploading mechanism: "Direct upload" and "Background upload".
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index d49d29c805a..1e8674f863d 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -84,11 +84,11 @@ In most of the below cases, the notification will be sent to:
- Participants:
- the author and assignee of the issue/merge request
- authors of comments on the issue/merge request
- - anyone mentioned by `@username` in the title or description of the issue, merge request or epic **[ULTIMATE]**
+ - anyone mentioned by `@username` in the title or description of the issue, merge request or epic **(ULTIMATE)**
- anyone with notification level "Participating" or higher that is mentioned by `@username`
- in any of the comments on the issue, merge request, or epic **[ULTIMATE]**
+ in any of the comments on the issue, merge request, or epic **(ULTIMATE)**
- Watchers: users with notification level "Watch"
-- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **[ULTIMATE]**
+- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **(ULTIMATE)**
- Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below
| Event | Sent to |
@@ -111,9 +111,9 @@ In most of the below cases, the notification will be sent to:
| New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher |
| Failed pipeline | The author of the pipeline |
| Successful pipeline | The author of the pipeline, if they have the custom notification setting for successful pipelines set |
-| New epic **[ULTIMATE]** | |
-| Close epic **[ULTIMATE]** | |
-| Reopen epic **[ULTIMATE]** | |
+| New epic **(ULTIMATE)** | |
+| Close epic **(ULTIMATE)** | |
+| Reopen epic **(ULTIMATE)** | |
In addition, if the title or description of an Issue or Merge Request is
changed, notifications will be sent to any **new** mentions by `@username` as
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index 5a24c254ed1..87ca46e94be 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -13,7 +13,7 @@ Repository mirroring is useful when you want to use a repository outside of GitL
There are two kinds of repository mirroring supported by GitLab:
- Push: for mirroring a GitLab repository to another location.
-- Pull: for mirroring a repository from another location to GitLab. **[STARTER]**
+- Pull: for mirroring a repository from another location to GitLab. **(STARTER)**
When the mirror repository is updated, all new branches, tags, and commits will be visible in the
project's activity feed.
@@ -30,12 +30,12 @@ The following are some possible use cases for repository mirroring:
- You migrated to GitLab but still need to keep your project in another source. In that case, you
can simply set it up to mirror to GitLab (pull) and all the essential history of commits, tags,
- and branches will be available in your GitLab instance. **[STARTER]**
+ and branches will be available in your GitLab instance. **(STARTER)**
- You have old projects in another source that you don't use actively anymore, but don't want to
remove for archiving purposes. In that case, you can create a push mirror so that your active
GitLab repository can push its changes to the old location.
-## Pushing to a remote repository **[CORE]**
+## Pushing to a remote repository **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in GitLab Enterprise
> Edition 8.7. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
@@ -65,7 +65,7 @@ Changes pushed to files in the repository are automatically pushed to the remote
In the case of a diverged branch, you will see an error indicated at the **Mirroring repositories**
section.
-### Push only protected branches **[CORE]**
+### Push only protected branches **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3350) in
> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3. [Moved to GitLab Core](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18715) in 10.8.
@@ -75,7 +75,7 @@ You can choose to only push your protected branches from GitLab to your remote r
To use this option, check the **Only mirror protected branches** box when creating a repository
mirror.
-## Setting up a push mirror from GitLab to GitHub **[CORE]**
+## Setting up a push mirror from GitLab to GitHub **(CORE)**
To set up a mirror from GitLab to GitHub, you need to follow these steps:
@@ -96,7 +96,7 @@ The repository will push soon. To force a push, click the appropriate button.
1. Fill in **Password** field with the GitLab personal access token created on the destination GitLab instance.
1. Click the **Mirror repository** button.
-## Pulling from a remote repository **[STARTER]**
+## Pulling from a remote repository **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2.
> [Added Git LFS support](https://gitlab.com/gitlab-org/gitlab-ee/issues/10871) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.11.
@@ -243,7 +243,7 @@ If you need to change the key at any time, you can remove and re-add the mirror
to generate a new key. You'll have to update the other repository with the new
key to keep the mirror running.
-### Overwrite diverged branches **[STARTER]**
+### Overwrite diverged branches **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4559) in
> [GitLab Starter](https://about.gitlab.com/pricing/) 10.6.
@@ -256,7 +256,7 @@ For mirrored branches, enabling this option results in the loss of local changes
To use this option, check the **Overwrite diverged branches** box when creating a repository mirror.
-### Only mirror protected branches **[STARTER]**
+### Only mirror protected branches **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3326) in
> [GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
@@ -266,7 +266,7 @@ Non-protected branches are not mirrored and can diverge.
To use this option, check the **Only mirror protected branches** box when creating a repository mirror.
-### Hard failure **[STARTER]**
+### Hard failure **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3117) in
> [GitLab Starter](https://about.gitlab.com/pricing/) 10.2.
@@ -280,25 +280,25 @@ failed. This will become visible in either the:
When a project is hard failed, it will no longer get picked up for mirroring. A user can resume the
project mirroring again by [Forcing an update](#forcing-an-update-core).
-### Trigger update using API **[STARTER]**
+### Trigger update using API **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3453) in
[GitLab Starter](https://about.gitlab.com/pricing/) 10.3.
Pull mirroring uses polling to detect new branches and commits added upstream, often minutes
-afterwards. If you notify GitLab by [API](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter),
+afterwards. If you notify GitLab by [API](../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter),
updates will be pulled immediately.
-For more information, see [Start the pull mirroring process for a Project](https://docs.gitlab.com/ee/api/projects.html#start-the-pull-mirroring-process-for-a-project-starter).
+For more information, see [Start the pull mirroring process for a Project](../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter).
-## Forcing an update **[CORE]**
+## Forcing an update **(CORE)**
While mirrors are scheduled to update automatically, you can always force an update by using the
update button which is available on the **Mirroring repositories** section of the **Repository Settings** page.
![Repository mirroring force update user interface](img/repository_mirroring_force_update.png)
-## Bidirectional mirroring **[STARTER]**
+## Bidirectional mirroring **(STARTER)**
CAUTION: **Caution:**
Bidirectional mirroring may cause conflicts.
@@ -395,7 +395,7 @@ else
fi
```
-### Mirroring with Perforce Helix via Git Fusion **[STARTER]**
+### Mirroring with Perforce Helix via Git Fusion **(STARTER)**
CAUTION: **Warning:**
Bidirectional mirroring should not be used as a permanent configuration. Refer to
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index 5068b5d4d20..fd67ea8ce87 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -85,7 +85,7 @@ You can see GitLab's keyboard shortcuts by using <kbd>shift</kbd> + <kbd>?</kbd>
| <kbd>]</kbd> or <kbd>j</kbd> | Move to next file |
| <kbd>[</kbd> or <kbd>k</kbd> | Move to previous file |
-## Epics **[ULTIMATE]**
+## Epics **(ULTIMATE)**
| Keyboard Shortcut | Description |
| ----------------- | ----------- |
diff --git a/doc/workflow/timezone.md b/doc/workflow/timezone.md
index da51c0f2c93..60a4d0f19de 100644
--- a/doc/workflow/timezone.md
+++ b/doc/workflow/timezone.md
@@ -2,13 +2,12 @@
The global time zone configuration parameter can be changed in `config/gitlab.yml`:
-```
+```text
# time_zone: 'UTC'
```
Uncomment and customize if you want to change the default time zone of the GitLab application.
-
## Viewing available timezones
To see all available time zones, run `bundle exec rake time:zones:all`.
@@ -26,14 +25,13 @@ To obtain a list of timezones, log in to your GitLab application server and run
To update, add the timezone that best applies to your location. For example:
-```
+```ruby
gitlab_rails['time_zone'] = 'America/New_York'
```
After adding the configuration parameter, reconfigure and restart your GitLab instance:
-```
+```sh
gitlab-ctl reconfigure
gitlab-ctl restart
```
-
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 3eac79427cf..1f8900c734b 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -1,50 +1,57 @@
-# GitLab Todos
+# GitLab To-Do List
> [Introduced][ce-2817] in GitLab 8.5.
When you log into GitLab, you normally want to see where you should spend your
-time and take some action, or what you need to keep an eye on. All without the
-mess of a huge pile of e-mail notifications. GitLab is where you do your work,
-so being able to get started quickly is very important.
+time, take some action, or know what you need to keep an eye on without
+a huge pile of e-mail notifications. GitLab is where you do your work,
+so being able to get started quickly is important.
-Todos is a chronological list of to-dos that are waiting for your input, all
+Your To-Do List offers a chronological list of items that are waiting for your input, all
in a simple dashboard.
-![Todos screenshot showing a list of items to check on](img/todos_index.png)
+![To Do screenshot showing a list of items to check on](img/todos_index.png)
---
-You can quickly access the Todos dashboard using the checkmark icon next to the
-search bar in the upper right corner. The number in blue is the number of Todos
-you still have open if the count is < 100, else it's 99+. The exact number
-will still be shown in the body of the _To do_ tab.
+You can quickly access your To-Do List by clicking the checkmark icon next to the
+search bar in the top navigation. If the count is:
-![Todos icon](img/todos_icon.png)
+- Less than 100, the number in blue is the number of To-Do items.
+- 100 or more, the number displays as 99+. The exact number displays
+ on the To-Do List.
+you still have open. Otherwise, the number displays as 99+. The exact number
+displays on the To-Do List.
-## What triggers a Todo
+![To Do icon](img/todos_icon.png)
-A Todo appears in your Todos dashboard when:
+## What triggers a To Do
-- an issue or merge request is assigned to you
-- you are `@mentioned` in the description or in a comment of an issue, merge request, or epic **[ULTIMATE]**
-- you are `@mentioned` in a comment on a commit,
-- a job in the CI pipeline running for your merge request failed, but this
- job is not allowed to fail.
-- an open merge request becomes unmergeable due to conflict, and you are either:
- - the author, or
- - have set it to automatically merge once pipeline succeeds.
+A To Do displays on your To-Do List when:
-Todo triggers are not affected by [GitLab Notification Email settings](notifications.md).
+- An issue or merge request is assigned to you
+- You are `@mentioned` in the description or comment of an:
+ - Issue
+ - Merge Request
+ - Epic **(ULTIMATE)**
+- You are `@mentioned` in a comment on a commit
+- A job in the CI pipeline running for your merge request failed, but this
+ job is not allowed to fail
+- An open merge request becomes unmergeable due to conflict, and you are either:
+ - The author
+ - Have set it to automatically merge once the pipeline succeeds
+
+To-do triggers are not affected by [GitLab Notification Email settings](notifications.md).
NOTE: **Note:**
-When an user no longer has access to a resource related to a Todo like an issue, merge request, project or group the related Todos, for security reasons, gets deleted within the next hour. The delete is delayed to prevent data loss in case user got their access revoked by mistake.
+When a user no longer has access to a resource related to a To Do (like an issue, merge request, project, or group) the related To-Do items are deleted within the next hour for security reasons. The delete is delayed to prevent data loss, in case the user's access was revoked by mistake.
-### Directly addressed Todos
+### Directly addressing a To Do
> [Introduced][ce-7926] in GitLab 9.0.
-If you are mentioned at the start of a line, the todo you receive will be listed
-as 'directly addressed'. For instance, in this comment:
+If you are mentioned at the start of a line, the To Do you receive will be listed
+as 'directly addressed'. For example, in this comment:
```markdown
@alice What do you think? cc: @bob
@@ -58,67 +65,80 @@ as 'directly addressed'. For instance, in this comment:
@erin @frank thank you!
```
-The people receiving directly addressed todos are `@alice`, `@erin`, and
-`@frank`. Directly addressed todos only differ from mention todos in their type,
-for filtering; otherwise, they appear as normal.
+The people receiving directly addressed To-Do items are `@alice`, `@erin`, and
+`@frank`. Directly addressed To-Do items only differ from mentions in their type
+for filtering purposes; otherwise, they appear as normal.
+
+### Manually creating a To Do
+
+You can also add the following to your To-Do List by clicking the **Add a To Do** button on an:
-### Manually creating a Todo
+- Issue
+- Merge Request
+- Epic **(ULTIMATE)**
-You can also add an issue, merge request or epic to your Todos dashboard by clicking
-the "Add todo" button in the sidebar of the issue, merge request, or epic **[ULTIMATE]**.
+![Adding a To Do from the issuable sidebar](img/todos_add_todo_sidebar.png)
-![Adding a Todo from the issuable sidebar](img/todos_add_todo_sidebar.png)
+## Marking a To Do as done
-## Marking a Todo as done
+Any action to the following will mark the corresponding To Do as done:
-Any action to the corresponding issue, merge request or epic **[ULTIMATE]** will mark your Todo as
-**Done**. Actions that dismiss Todos include:
+- Issue
+- Merge Request
+- Epic **(ULTIMATE)**
-- changing the assignee
-- changing the milestone
-- adding/removing a label
-- commenting on the issue
+Actions that dismiss To-Do items include:
+
+- Changing the assignee
+- Changing the milestone
+- Adding/removing a label
+- Commenting on the issue
---
-Todos are personal, and they're only marked as done if the action is coming from
-you. If you close the issue or merge request, your Todo will automatically
-be marked as done.
+Your To-Do List is personal, and items are only marked as done if the action comes from
+you. If you close the issue or merge request, your To Do is automatically
+marked as done.
+
+To prevent other users from closing issues without you being notified, if someone else closes, merges, or takes action on the any of the following, your To Do will remain pending:
-If someone else closes, merges, or takes action on the issue, epic or merge
-request, your Todo will remain pending. This prevents other users from closing issues without you being notified.
+- Issue
+- Merge request
+- Epic **(ULTIMATE)**
-There is just one Todo per issue, epic or merge request, so mentioning a user a
-hundred times in an issue will only trigger one Todo.
+There is just one To Do for each of these, so mentioning a user a hundred times in an issue will only trigger one To Do.
---
-If no action is needed, you can manually mark the Todo as done by clicking the
-corresponding **Done** button, and it will disappear from your Todo list.
+If no action is needed, you can manually mark the To Do as done by clicking the
+corresponding **Done** button, and it will disappear from your To-Do List.
+
+![A To Do in the To-Do List](img/todo_list_item.png)
-![A Todo in the Todos dashboard](img/todo_list_item.png)
+You can also mark a To Do as done by clicking the **Mark as done** button in the sidebar of the following:
-A Todo can also be marked as done from the issue, merge request or epic sidebar using
-the "Mark todo as done" button.
+- Issue
+- Merge Request
+- Epic **(ULTIMATE)**
-![Mark todo as done from the issuable sidebar](img/todos_mark_done_sidebar.png)
+![Mark as done from the issuable sidebar](img/todos_mark_done_sidebar.png)
-You can mark all your Todos as done at once by clicking on the **Mark all as
+You can mark all your To-Do items as done at once by clicking the **Mark all as
done** button.
-## Filtering your Todos
+## Filtering your To-Do List
-There are four kinds of filters you can use on your Todos dashboard.
+There are four kinds of filters you can use on your To-Do List.
| Filter | Description |
| ------- | ----------- |
| Project | Filter by project |
| Group | Filter by group |
-| Author | Filter by the author that triggered the Todo |
-| Type | Filter by issue, merge request, or epic **[ULTIMATE]** |
-| Action | Filter by the action that triggered the Todo |
+| Author | Filter by the author that triggered the To Do |
+| Type | Filter by issue, merge request, or epic **(ULTIMATE)** |
+| Action | Filter by the action that triggered the To Do |
-You can also filter by more than one of these at the same time. The possible Actions are `Any Action`, `Assigned`, `Mentioned`, `Added`, `Pipelines`, and `Directly Addressed`, [as described above](#what-triggers-a-todo).
+You can also filter by more than one of these at the same time. The possible Actions are `Any Action`, `Assigned`, `Mentioned`, `Added`, `Pipelines`, and `Directly Addressed`, [as described above](#what-triggers-a-to-do).
[ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817
[ce-7926]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7926
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 20f8c637274..7016a66593d 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -52,7 +52,10 @@ module API
rack_response({ 'message' => '404 Not found' }.to_json, 404)
end
- rescue_from ::Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError do
+ rescue_from(
+ ::ActiveRecord::StaleObjectError,
+ ::Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
+ ) do
rack_response({ 'message' => '409 Conflict: Resource lock' }.to_json, 409)
end
@@ -163,6 +166,7 @@ module API
mount ::API::Templates
mount ::API::Todos
mount ::API::Triggers
+ mount ::API::UserCounts
mount ::API::Users
mount ::API::Variables
mount ::API::Version
diff --git a/lib/api/helpers/graphql_helpers.rb b/lib/api/helpers/graphql_helpers.rb
index 94010ab1bc2..bd60470fbd6 100644
--- a/lib/api/helpers/graphql_helpers.rb
+++ b/lib/api/helpers/graphql_helpers.rb
@@ -7,8 +7,6 @@ module API
# should be in app/graphql/ or lib/gitlab/graphql/
module GraphqlHelpers
def conditionally_graphql!(fallback:, query:, context: {}, transform: nil)
- return fallback.call unless Feature.enabled?(:graphql)
-
result = GitlabSchema.execute(query, context: context)
if transform
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 6b8c1a2c0e8..64ee82cd775 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -429,9 +429,10 @@ module API
authorize_push_to_merge_request!(merge_request)
- RebaseWorker.perform_async(merge_request.id, current_user.id)
+ merge_request.rebase_async(current_user.id)
status :accepted
+ present rebase_in_progress: merge_request.rebase_in_progress?
end
desc 'List issues that will be closed on merge' do
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 1e14c77b5be..a7d62014509 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -474,7 +474,7 @@ module API
authorize_admin_project
begin
- ::Projects::HousekeepingService.new(user_project).execute
+ ::Projects::HousekeepingService.new(user_project, :gc).execute
rescue ::Projects::HousekeepingService::LeaseTaken => error
conflict!(error.message)
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index f3fea463e7f..c2d371b6867 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -115,6 +115,8 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of the runner'
optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES
+ optional :order_by, type: String, desc: 'Order by `id` or not', values: RunnerJobsFinder::ALLOWED_INDEXED_COLUMNS
+ optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
get ':id/jobs' do
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 3c5c1a9fd5f..4275d911708 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -55,6 +55,8 @@ module API
optional :gitaly_timeout_default, type: Integer, desc: 'Default Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
optional :gitaly_timeout_fast, type: Integer, desc: 'Gitaly fast operation timeout, in seconds. Set to 0 to disable timeouts.'
optional :gitaly_timeout_medium, type: Integer, desc: 'Medium Gitaly timeout, in seconds. Set to 0 to disable timeouts.'
+ optional :grafana_enabled, type: Boolean, desc: 'Enable Grafana'
+ optional :grafana_url, type: String, desc: 'Grafana URL'
optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
optional :help_page_hide_commercial_content, type: Boolean, desc: 'Hide marketing-related entries from help'
optional :help_page_support_url, type: String, desc: 'Alternate support URL for help page'
diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb
new file mode 100644
index 00000000000..8df4b381bbf
--- /dev/null
+++ b/lib/api/user_counts.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module API
+ class UserCounts < Grape::API
+ resource :user_counts do
+ desc 'Return the user specific counts' do
+ detail 'Open MR Count'
+ end
+ get do
+ unauthorized! unless current_user
+
+ {
+ merge_requests: current_user.assigned_open_merge_requests_count
+ }
+ end
+ end
+ end
+end
diff --git a/lib/feature.rb b/lib/feature.rb
index 22420e95ea2..e28333aa58e 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -103,10 +103,27 @@ class Feature
feature_class: FlipperFeature,
gate_class: FlipperGate)
+ # Redis L2 cache
+ redis_cache_adapter =
+ Flipper::Adapters::ActiveSupportCacheStore.new(
+ active_record_adapter,
+ l2_cache_backend,
+ expires_in: 1.hour)
+
+ # Thread-local L1 cache: use a short timeout since we don't have a
+ # way to expire this cache all at once
Flipper::Adapters::ActiveSupportCacheStore.new(
- active_record_adapter,
- Rails.cache,
- expires_in: 1.hour)
+ redis_cache_adapter,
+ l1_cache_backend,
+ expires_in: 1.minute)
+ end
+
+ def l1_cache_backend
+ Gitlab::ThreadMemoryCache.cache_backend
+ end
+
+ def l2_cache_backend
+ Rails.cache
end
end
diff --git a/lib/feature/gitaly.rb b/lib/feature/gitaly.rb
index d7a8f8a0b9e..67c0b902c0c 100644
--- a/lib/feature/gitaly.rb
+++ b/lib/feature/gitaly.rb
@@ -8,7 +8,12 @@ class Feature
# CATFILE_CACHE sets an incorrect example
CATFILE_CACHE = 'catfile-cache'.freeze
- SERVER_FEATURE_FLAGS = [CATFILE_CACHE].freeze
+ SERVER_FEATURE_FLAGS =
+ [
+ CATFILE_CACHE,
+ 'get_commit_signatures'.freeze
+ ].freeze
+
DEFAULT_ON_FLAGS = Set.new([CATFILE_CACHE]).freeze
class << self
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index dcf8254ef94..108f0119ae1 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -246,7 +246,6 @@ rollout 100%:
auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}
export DATABASE_URL=${DATABASE_URL-$auto_database_url}
export TILLER_NAMESPACE=$KUBE_NAMESPACE
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
function get_replicas() {
track="${1:-stable}"
diff --git a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
index b9fee2d5731..25ea20e454f 100644
--- a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
@@ -1,5 +1,5 @@
# Select image from https://hub.docker.com/_/php/
-image: php:7.1.1
+image: php:latest
# Select what we should cache between builds
cache:
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index 8713b833011..0a97a16b83c 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -54,6 +54,7 @@ sast:
MAVEN_PATH \
MAVEN_REPO_PATH \
SBT_PATH \
+ FAIL_NEVER \
) \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index 1ecf4a12db7..0fc145534bf 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -103,6 +103,11 @@ module Gitlab
yarn\.lock
)\z}x => :frontend,
+ %r{\A(ee/)?db/} => :database,
+ %r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database,
+ %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database,
+ %r{\Arubocop/cop/migration(/|\.rb)} => :database,
+
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|danger|generator_templates|lib|rubocop|scripts)/} => :backend,
%r{\A(ee/)?spec/features/} => :test,
@@ -112,7 +117,6 @@ module Gitlab
%r{\A(Dangerfile|Gemfile|Gemfile.lock|Procfile|Rakefile|\.gitlab-ci\.yml)\z} => :backend,
%r{\A[A-Z_]+_VERSION\z} => :backend,
- %r{\A(ee/)?db/} => :database,
%r{\A(ee/)?qa/} => :qa,
# Files that don't fit into any category are marked with :none
diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb
index 1455e410d4b..b8d895dee7d 100644
--- a/lib/gitlab/database/median.rb
+++ b/lib/gitlab/database/median.rb
@@ -158,7 +158,7 @@ module Gitlab
Arel::Nodes::Window.new.order(arel_table[column_sym])
).as('row_id')
- count = arel_table.project("COUNT(1)").as('ct')
+ count = arel_table.where(arel_table[column_sym].gteq(zero_interval)).project("COUNT(1)").as('ct')
[column_row, row_id, count]
end
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index d349c378e53..dfa80eb4a64 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -134,6 +134,10 @@ module Gitlab
@line_code ||= diff_file(repository)&.line_code_for_position(self)
end
+ def file_hash
+ @file_hash ||= Digest::SHA1.hexdigest(file_path)
+ end
+
def on_image?
position_type == 'image'
end
diff --git a/lib/gitlab/diff/position_tracer.rb b/lib/gitlab/diff/position_tracer.rb
index af3df820422..a1c82ce9afc 100644
--- a/lib/gitlab/diff/position_tracer.rb
+++ b/lib/gitlab/diff/position_tracer.rb
@@ -17,187 +17,13 @@ module Gitlab
@paths = paths
end
- def trace(ab_position)
+ def trace(old_position)
return unless old_diff_refs&.complete? && new_diff_refs&.complete?
- return unless ab_position.diff_refs == old_diff_refs
+ return unless old_position.diff_refs == old_diff_refs
- # Suppose we have an MR with source branch `feature` and target branch `master`.
- # When the MR was created, the head of `master` was commit A, and the
- # head of `feature` was commit B, resulting in the original diff A->B.
- # Since creation, `master` was updated to C.
- # Now `feature` is being updated to D, and the newly generated MR diff is C->D.
- # It is possible that C and D are direct descendants of A and B respectively,
- # but this isn't necessarily the case as rebases and merges come into play.
- #
- # Suppose we have a diff note on the original diff A->B. Now that the MR
- # is updated, we need to find out what line in C->D corresponds to the
- # line the note was originally created on, so that we can update the diff note's
- # records and continue to display it in the right place in the diffs.
- # If we cannot find this line in the new diff, this means the diff note is now
- # outdated, and we will display that fact to the user.
- #
- # In the new diff, the file the diff note was originally created on may
- # have been renamed, deleted or even created, if the file existed in A and B,
- # but was removed in C, and restored in D.
- #
- # Every diff note stores a Position object that defines a specific location,
- # identified by paths and line numbers, within a specific diff, identified
- # by start, head and base commit ids.
- #
- # For diff notes for diff A->B, the position looks like this:
- # Position
- # start_sha - ID of commit A
- # head_sha - ID of commit B
- # base_sha - ID of base commit of A and B
- # old_path - path as of A (nil if file was newly created)
- # new_path - path as of B (nil if file was deleted)
- # old_line - line number as of A (nil if file was newly created)
- # new_line - line number as of B (nil if file was deleted)
- #
- # We can easily update `start_sha` and `head_sha` to hold the IDs of
- # commits C and D, and can trivially determine `base_sha` based on those,
- # but need to find the paths and line numbers as of C and D.
- #
- # If the file was unchanged or newly created in A->B, the path as of D can be found
- # by generating diff B->D ("head to head"), finding the diff file with
- # `diff_file.old_path == position.new_path`, and taking `diff_file.new_path`.
- # The path as of C can be found by taking diff C->D, finding the diff file
- # with that same `new_path` and taking `diff_file.old_path`.
- # The line number as of D can be found by using the LineMapper on diff B->D
- # and providing the line number as of B.
- # The line number as of C can be found by using the LineMapper on diff C->D
- # and providing the line number as of D.
- #
- # If the file was deleted in A->B, the path as of C can be found
- # by generating diff A->C ("base to base"), finding the diff file with
- # `diff_file.old_path == position.old_path`, and taking `diff_file.new_path`.
- # The path as of D can be found by taking diff C->D, finding the diff file
- # with `old_path` set to that `diff_file.new_path` and taking `diff_file.new_path`.
- # The line number as of C can be found by using the LineMapper on diff A->C
- # and providing the line number as of A.
- # The line number as of D can be found by using the LineMapper on diff C->D
- # and providing the line number as of C.
+ strategy = old_position.on_text? ? LineStrategy : ImageStrategy
- if ab_position.added?
- trace_added_line(ab_position)
- elsif ab_position.removed?
- trace_removed_line(ab_position)
- else # unchanged
- trace_unchanged_line(ab_position)
- end
- end
-
- private
-
- def trace_added_line(ab_position)
- b_path = ab_position.new_path
- b_line = ab_position.new_line
-
- bd_diff = bd_diffs.diff_file_with_old_path(b_path)
-
- d_path = bd_diff&.new_path || b_path
- d_line = LineMapper.new(bd_diff).old_to_new(b_line)
-
- if d_line
- cd_diff = cd_diffs.diff_file_with_new_path(d_path)
-
- c_path = cd_diff&.old_path || d_path
- c_line = LineMapper.new(cd_diff).new_to_old(d_line)
-
- if c_line
- # If the line is still in D but also in C, it has turned from an
- # added line into an unchanged one.
- new_position = position(cd_diff, c_line, d_line)
- if valid_position?(new_position)
- # If the line is still in the MR, we don't treat this as outdated.
- { position: new_position, outdated: false }
- else
- # If the line is no longer in the MR, we unfortunately cannot show
- # the current state on the CD diff, so we treat it as outdated.
- ac_diff = ac_diffs.diff_file_with_new_path(c_path)
-
- { position: position(ac_diff, nil, c_line), outdated: true }
- end
- else
- # If the line is still in D and not in C, it is still added.
- { position: position(cd_diff, nil, d_line), outdated: false }
- end
- else
- # If the line is no longer in D, it has been removed from the MR.
- { position: position(bd_diff, b_line, nil), outdated: true }
- end
- end
-
- def trace_removed_line(ab_position)
- a_path = ab_position.old_path
- a_line = ab_position.old_line
-
- ac_diff = ac_diffs.diff_file_with_old_path(a_path)
-
- c_path = ac_diff&.new_path || a_path
- c_line = LineMapper.new(ac_diff).old_to_new(a_line)
-
- if c_line
- cd_diff = cd_diffs.diff_file_with_old_path(c_path)
-
- d_path = cd_diff&.new_path || c_path
- d_line = LineMapper.new(cd_diff).old_to_new(c_line)
-
- if d_line
- # If the line is still in C but also in D, it has turned from a
- # removed line into an unchanged one.
- bd_diff = bd_diffs.diff_file_with_new_path(d_path)
-
- { position: position(bd_diff, nil, d_line), outdated: true }
- else
- # If the line is still in C and not in D, it is still removed.
- { position: position(cd_diff, c_line, nil), outdated: false }
- end
- else
- # If the line is no longer in C, it has been removed outside of the MR.
- { position: position(ac_diff, a_line, nil), outdated: true }
- end
- end
-
- def trace_unchanged_line(ab_position)
- a_path = ab_position.old_path
- a_line = ab_position.old_line
- b_path = ab_position.new_path
- b_line = ab_position.new_line
-
- ac_diff = ac_diffs.diff_file_with_old_path(a_path)
-
- c_path = ac_diff&.new_path || a_path
- c_line = LineMapper.new(ac_diff).old_to_new(a_line)
-
- bd_diff = bd_diffs.diff_file_with_old_path(b_path)
-
- d_line = LineMapper.new(bd_diff).old_to_new(b_line)
-
- cd_diff = cd_diffs.diff_file_with_old_path(c_path)
-
- if c_line && d_line
- # If the line is still in C and D, it is still unchanged.
- new_position = position(cd_diff, c_line, d_line)
- if valid_position?(new_position)
- # If the line is still in the MR, we don't treat this as outdated.
- { position: new_position, outdated: false }
- else
- # If the line is no longer in the MR, we unfortunately cannot show
- # the current state on the CD diff or any change on the BD diff,
- # so we treat it as outdated.
- { position: nil, outdated: true }
- end
- elsif d_line # && !c_line
- # If the line is still in D but no longer in C, it has turned from
- # an unchanged line into an added one.
- # We don't treat this as outdated since the line is still in the MR.
- { position: position(cd_diff, nil, d_line), outdated: false }
- else # !d_line && (c_line || !c_line)
- # If the line is no longer in D, it has turned from an unchanged line
- # into a removed one.
- { position: position(bd_diff, b_line, nil), outdated: true }
- end
+ strategy.new(self).trace(old_position)
end
def ac_diffs
@@ -216,18 +42,12 @@ module Gitlab
@cd_diffs ||= compare(new_diff_refs.start_sha, new_diff_refs.head_sha)
end
+ private
+
def compare(start_sha, head_sha, straight: false)
compare = CompareService.new(project, head_sha).execute(project, start_sha, straight: straight)
compare.diffs(paths: paths, expanded: true)
end
-
- def position(diff_file, old_line, new_line)
- Position.new(diff_file: diff_file, old_line: old_line, new_line: new_line)
- end
-
- def valid_position?(position)
- !!position.diff_line(project.repository)
- end
end
end
end
diff --git a/lib/gitlab/diff/position_tracer/base_strategy.rb b/lib/gitlab/diff/position_tracer/base_strategy.rb
new file mode 100644
index 00000000000..65049daabf4
--- /dev/null
+++ b/lib/gitlab/diff/position_tracer/base_strategy.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class PositionTracer
+ class BaseStrategy
+ attr_reader :tracer
+
+ delegate \
+ :project,
+ :ac_diffs,
+ :bd_diffs,
+ :cd_diffs,
+ to: :tracer
+
+ def initialize(tracer)
+ @tracer = tracer
+ end
+
+ def trace(position)
+ raise NotImplementedError
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/position_tracer/image_strategy.rb b/lib/gitlab/diff/position_tracer/image_strategy.rb
new file mode 100644
index 00000000000..79244a17951
--- /dev/null
+++ b/lib/gitlab/diff/position_tracer/image_strategy.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class PositionTracer
+ class ImageStrategy < BaseStrategy
+ def trace(position)
+ b_path = position.new_path
+
+ # If file exists in B->D (e.g. updated, renamed, removed), let the
+ # note become outdated.
+ bd_diff = bd_diffs.diff_file_with_old_path(b_path)
+
+ return { position: new_position(position, bd_diff), outdated: true } if bd_diff
+
+ # If file still exists in the new diff, update the position.
+ cd_diff = cd_diffs.diff_file_with_new_path(bd_diff&.new_path || b_path)
+
+ return { position: new_position(position, cd_diff), outdated: false } if cd_diff
+
+ # If file exists in A->C (e.g. rebased and same changes were present
+ # in target branch), let the note become outdated.
+ ac_diff = ac_diffs.diff_file_with_old_path(position.old_path)
+
+ return { position: new_position(position, ac_diff), outdated: true } if ac_diff
+
+ # If ever there's a case that the file no longer exists in any diff,
+ # don't set a change position and let the note become outdated.
+ #
+ # This should never happen given the file should exist in one of the
+ # diffs above.
+ { outdated: true }
+ end
+
+ private
+
+ def new_position(position, diff_file)
+ Position.new(
+ diff_file: diff_file,
+ x: position.x,
+ y: position.y,
+ width: position.width,
+ height: position.height,
+ position_type: position.position_type
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/position_tracer/line_strategy.rb b/lib/gitlab/diff/position_tracer/line_strategy.rb
new file mode 100644
index 00000000000..8db0fc6f963
--- /dev/null
+++ b/lib/gitlab/diff/position_tracer/line_strategy.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class PositionTracer
+ class LineStrategy < BaseStrategy
+ def trace(position)
+ # Suppose we have an MR with source branch `feature` and target branch `master`.
+ # When the MR was created, the head of `master` was commit A, and the
+ # head of `feature` was commit B, resulting in the original diff A->B.
+ # Since creation, `master` was updated to C.
+ # Now `feature` is being updated to D, and the newly generated MR diff is C->D.
+ # It is possible that C and D are direct descendants of A and B respectively,
+ # but this isn't necessarily the case as rebases and merges come into play.
+ #
+ # Suppose we have a diff note on the original diff A->B. Now that the MR
+ # is updated, we need to find out what line in C->D corresponds to the
+ # line the note was originally created on, so that we can update the diff note's
+ # records and continue to display it in the right place in the diffs.
+ # If we cannot find this line in the new diff, this means the diff note is now
+ # outdated, and we will display that fact to the user.
+ #
+ # In the new diff, the file the diff note was originally created on may
+ # have been renamed, deleted or even created, if the file existed in A and B,
+ # but was removed in C, and restored in D.
+ #
+ # Every diff note stores a Position object that defines a specific location,
+ # identified by paths and line numbers, within a specific diff, identified
+ # by start, head and base commit ids.
+ #
+ # For diff notes for diff A->B, the position looks like this:
+ # Position
+ # start_sha - ID of commit A
+ # head_sha - ID of commit B
+ # base_sha - ID of base commit of A and B
+ # old_path - path as of A (nil if file was newly created)
+ # new_path - path as of B (nil if file was deleted)
+ # old_line - line number as of A (nil if file was newly created)
+ # new_line - line number as of B (nil if file was deleted)
+ #
+ # We can easily update `start_sha` and `head_sha` to hold the IDs of
+ # commits C and D, and can trivially determine `base_sha` based on those,
+ # but need to find the paths and line numbers as of C and D.
+ #
+ # If the file was unchanged or newly created in A->B, the path as of D can be found
+ # by generating diff B->D ("head to head"), finding the diff file with
+ # `diff_file.old_path == position.new_path`, and taking `diff_file.new_path`.
+ # The path as of C can be found by taking diff C->D, finding the diff file
+ # with that same `new_path` and taking `diff_file.old_path`.
+ # The line number as of D can be found by using the LineMapper on diff B->D
+ # and providing the line number as of B.
+ # The line number as of C can be found by using the LineMapper on diff C->D
+ # and providing the line number as of D.
+ #
+ # If the file was deleted in A->B, the path as of C can be found
+ # by generating diff A->C ("base to base"), finding the diff file with
+ # `diff_file.old_path == position.old_path`, and taking `diff_file.new_path`.
+ # The path as of D can be found by taking diff C->D, finding the diff file
+ # with `old_path` set to that `diff_file.new_path` and taking `diff_file.new_path`.
+ # The line number as of C can be found by using the LineMapper on diff A->C
+ # and providing the line number as of A.
+ # The line number as of D can be found by using the LineMapper on diff C->D
+ # and providing the line number as of C.
+
+ if position.added?
+ trace_added_line(position)
+ elsif position.removed?
+ trace_removed_line(position)
+ else # unchanged
+ trace_unchanged_line(position)
+ end
+ end
+
+ private
+
+ def trace_added_line(position)
+ b_path = position.new_path
+ b_line = position.new_line
+
+ bd_diff = bd_diffs.diff_file_with_old_path(b_path)
+
+ d_path = bd_diff&.new_path || b_path
+ d_line = LineMapper.new(bd_diff).old_to_new(b_line)
+
+ if d_line
+ cd_diff = cd_diffs.diff_file_with_new_path(d_path)
+
+ c_path = cd_diff&.old_path || d_path
+ c_line = LineMapper.new(cd_diff).new_to_old(d_line)
+
+ if c_line
+ # If the line is still in D but also in C, it has turned from an
+ # added line into an unchanged one.
+ new_position = new_position(cd_diff, c_line, d_line)
+ if valid_position?(new_position)
+ # If the line is still in the MR, we don't treat this as outdated.
+ { position: new_position, outdated: false }
+ else
+ # If the line is no longer in the MR, we unfortunately cannot show
+ # the current state on the CD diff, so we treat it as outdated.
+ ac_diff = ac_diffs.diff_file_with_new_path(c_path)
+
+ { position: new_position(ac_diff, nil, c_line), outdated: true }
+ end
+ else
+ # If the line is still in D and not in C, it is still added.
+ { position: new_position(cd_diff, nil, d_line), outdated: false }
+ end
+ else
+ # If the line is no longer in D, it has been removed from the MR.
+ { position: new_position(bd_diff, b_line, nil), outdated: true }
+ end
+ end
+
+ def trace_removed_line(position)
+ a_path = position.old_path
+ a_line = position.old_line
+
+ ac_diff = ac_diffs.diff_file_with_old_path(a_path)
+
+ c_path = ac_diff&.new_path || a_path
+ c_line = LineMapper.new(ac_diff).old_to_new(a_line)
+
+ if c_line
+ cd_diff = cd_diffs.diff_file_with_old_path(c_path)
+
+ d_path = cd_diff&.new_path || c_path
+ d_line = LineMapper.new(cd_diff).old_to_new(c_line)
+
+ if d_line
+ # If the line is still in C but also in D, it has turned from a
+ # removed line into an unchanged one.
+ bd_diff = bd_diffs.diff_file_with_new_path(d_path)
+
+ { position: new_position(bd_diff, nil, d_line), outdated: true }
+ else
+ # If the line is still in C and not in D, it is still removed.
+ { position: new_position(cd_diff, c_line, nil), outdated: false }
+ end
+ else
+ # If the line is no longer in C, it has been removed outside of the MR.
+ { position: new_position(ac_diff, a_line, nil), outdated: true }
+ end
+ end
+
+ def trace_unchanged_line(position)
+ a_path = position.old_path
+ a_line = position.old_line
+ b_path = position.new_path
+ b_line = position.new_line
+
+ ac_diff = ac_diffs.diff_file_with_old_path(a_path)
+
+ c_path = ac_diff&.new_path || a_path
+ c_line = LineMapper.new(ac_diff).old_to_new(a_line)
+
+ bd_diff = bd_diffs.diff_file_with_old_path(b_path)
+
+ d_line = LineMapper.new(bd_diff).old_to_new(b_line)
+
+ cd_diff = cd_diffs.diff_file_with_old_path(c_path)
+
+ if c_line && d_line
+ # If the line is still in C and D, it is still unchanged.
+ new_position = new_position(cd_diff, c_line, d_line)
+ if valid_position?(new_position)
+ # If the line is still in the MR, we don't treat this as outdated.
+ { position: new_position, outdated: false }
+ else
+ # If the line is no longer in the MR, we unfortunately cannot show
+ # the current state on the CD diff or any change on the BD diff,
+ # so we treat it as outdated.
+ { position: nil, outdated: true }
+ end
+ elsif d_line # && !c_line
+ # If the line is still in D but no longer in C, it has turned from
+ # an unchanged line into an added one.
+ # We don't treat this as outdated since the line is still in the MR.
+ { position: new_position(cd_diff, nil, d_line), outdated: false }
+ else # !d_line && (c_line || !c_line)
+ # If the line is no longer in D, it has turned from an unchanged line
+ # into a removed one.
+ { position: new_position(bd_diff, b_line, nil), outdated: true }
+ end
+ end
+
+ def new_position(diff_file, old_line, new_line)
+ Position.new(
+ diff_file: diff_file,
+ old_line: old_line,
+ new_line: new_line
+ )
+ end
+
+ def valid_position?(position)
+ !!position.diff_line(project.repository)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 19b6aab1c4f..060a29be782 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -536,9 +536,9 @@ module Gitlab
tags.find { |tag| tag.name == name }
end
- def merge_to_ref(user, source_sha, branch, target_ref, message)
+ def merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
wrapped_gitaly_errors do
- gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message)
+ gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
end
end
diff --git a/lib/gitlab/git/rugged_impl/blob.rb b/lib/gitlab/git/rugged_impl/blob.rb
index 11ee4ebda4b..86c9f33d82a 100644
--- a/lib/gitlab/git/rugged_impl/blob.rb
+++ b/lib/gitlab/git/rugged_impl/blob.rb
@@ -11,10 +11,11 @@ module Gitlab
module Blob
module ClassMethods
extend ::Gitlab::Utils::Override
+ include Gitlab::Git::RuggedImpl::UseRugged
override :tree_entry
def tree_entry(repository, sha, path, limit)
- if Feature.enabled?(:rugged_tree_entry)
+ if use_rugged?(repository, :rugged_tree_entry)
rugged_tree_entry(repository, sha, path, limit)
else
super
diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb
index bce4fa14fb4..971a33b2e99 100644
--- a/lib/gitlab/git/rugged_impl/commit.rb
+++ b/lib/gitlab/git/rugged_impl/commit.rb
@@ -12,6 +12,7 @@ module Gitlab
module Commit
module ClassMethods
extend ::Gitlab::Utils::Override
+ include Gitlab::Git::RuggedImpl::UseRugged
def rugged_find(repo, commit_id)
obj = repo.rev_parse_target(commit_id)
@@ -34,7 +35,7 @@ module Gitlab
override :find_commit
def find_commit(repo, commit_id)
- if Feature.enabled?(:rugged_find_commit)
+ if use_rugged?(repo, :rugged_find_commit)
rugged_find(repo, commit_id)
else
super
@@ -43,7 +44,7 @@ module Gitlab
override :batch_by_oid
def batch_by_oid(repo, oids)
- if Feature.enabled?(:rugged_list_commits_by_oid)
+ if use_rugged?(repo, :rugged_list_commits_by_oid)
rugged_batch_by_oid(repo, oids)
else
super
@@ -52,6 +53,7 @@ module Gitlab
end
extend ::Gitlab::Utils::Override
+ include Gitlab::Git::RuggedImpl::UseRugged
override :init_commit
def init_commit(raw_commit)
@@ -65,7 +67,7 @@ module Gitlab
override :commit_tree_entry
def commit_tree_entry(path)
- if Feature.enabled?(:rugged_commit_tree_entry)
+ if use_rugged?(@repository, :rugged_commit_tree_entry)
rugged_tree_entry(path)
else
super
diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb
index e91b0ddcd31..9268abdfed9 100644
--- a/lib/gitlab/git/rugged_impl/repository.rb
+++ b/lib/gitlab/git/rugged_impl/repository.rb
@@ -11,6 +11,7 @@ module Gitlab
module RuggedImpl
module Repository
extend ::Gitlab::Utils::Override
+ include Gitlab::Git::RuggedImpl::UseRugged
FEATURE_FLAGS = %i(rugged_find_commit rugged_tree_entries rugged_tree_entry rugged_commit_is_ancestor rugged_commit_tree_entry rugged_list_commits_by_oid).freeze
@@ -46,7 +47,7 @@ module Gitlab
override :ancestor?
def ancestor?(from, to)
- if Feature.enabled?(:rugged_commit_is_ancestor)
+ if use_rugged?(self, :rugged_commit_is_ancestor)
rugged_is_ancestor?(from, to)
else
super
diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb
index 9c37bb01961..f3721a3f1b7 100644
--- a/lib/gitlab/git/rugged_impl/tree.rb
+++ b/lib/gitlab/git/rugged_impl/tree.rb
@@ -11,10 +11,11 @@ module Gitlab
module Tree
module ClassMethods
extend ::Gitlab::Utils::Override
+ include Gitlab::Git::RuggedImpl::UseRugged
override :tree_entries
def tree_entries(repository, sha, path, recursive)
- if Feature.enabled?(:rugged_tree_entries)
+ if use_rugged?(repository, :rugged_tree_entries)
tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive)
else
super
diff --git a/lib/gitlab/git/rugged_impl/use_rugged.rb b/lib/gitlab/git/rugged_impl/use_rugged.rb
new file mode 100644
index 00000000000..99091b03cd1
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/use_rugged.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Git
+ module RuggedImpl
+ module UseRugged
+ def use_rugged?(repo, feature_key)
+ feature = Feature.get(feature_key)
+ return feature.enabled? if Feature.persisted?(feature)
+
+ Gitlab::GitalyClient.can_use_disk?(repo.storage)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 47976389af6..cf0157269a8 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -30,14 +30,10 @@ module Gitlab
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'
MAXIMUM_GITALY_CALLS = 30
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
+ GITALY_METADATA_FILENAME = '.gitaly-metadata'
MUTEX = Mutex.new
- define_histogram :gitaly_controller_action_duration_seconds do
- docstring "Gitaly endpoint histogram by controller and action combination"
- base_labels Gitlab::Metrics::Transaction::BASE_LABELS.merge(gitaly_service: nil, rpc: nil)
- end
-
def self.stub(name, storage)
MUTEX.synchronize do
@stubs ||= {}
@@ -161,10 +157,6 @@ module Gitlab
# Keep track, separately, for the performance bar
self.query_time += duration
- gitaly_controller_action_duration_seconds.observe(
- current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s),
- duration)
-
if peek_enabled?
add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc,
backtrace: Gitlab::Profiler.clean_backtrace(caller))
@@ -387,6 +379,45 @@ module Gitlab
0
end
+ def self.storage_metadata_file_path(storage)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ File.join(
+ Gitlab.config.repositories.storages[storage].legacy_disk_path, GITALY_METADATA_FILENAME
+ )
+ end
+ end
+
+ def self.can_use_disk?(storage)
+ cached_value = MUTEX.synchronize do
+ @can_use_disk ||= {}
+ @can_use_disk[storage]
+ end
+
+ return cached_value unless cached_value.nil?
+
+ gitaly_filesystem_id = filesystem_id(storage)
+ direct_filesystem_id = filesystem_id_from_disk(storage)
+
+ MUTEX.synchronize do
+ @can_use_disk[storage] = gitaly_filesystem_id.present? &&
+ gitaly_filesystem_id == direct_filesystem_id
+ end
+ end
+
+ def self.filesystem_id(storage)
+ response = Gitlab::GitalyClient::ServerService.new(storage).info
+ storage_status = response.storage_statuses.find { |status| status.storage_name == storage }
+ storage_status.filesystem_id
+ end
+
+ def self.filesystem_id_from_disk(storage)
+ metadata_file = File.read(storage_metadata_file_path(storage))
+ metadata_hash = JSON.parse(metadata_file)
+ metadata_hash['gitaly_filesystem_id']
+ rescue Errno::ENOENT, JSON::ParserError
+ nil
+ end
+
def self.timeout(timeout_name)
Gitlab::CurrentSettings.current_application_settings[timeout_name]
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index b42e6cbad8d..783c2ff0915 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -100,14 +100,15 @@ module Gitlab
end
end
- def user_merge_to_ref(user, source_sha, branch, target_ref, message)
+ def user_merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref)
request = Gitaly::UserMergeToRefRequest.new(
repository: @gitaly_repo,
source_sha: source_sha,
branch: encode_binary(branch),
target_ref: encode_binary(target_ref),
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
- message: encode_binary(message)
+ message: encode_binary(message),
+ first_parent_ref: encode_binary(first_parent_ref)
)
response = GitalyClient.call(@repository.storage, :operation_service, :user_merge_to_ref, request)
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 92917028851..41ec8741eb1 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -38,6 +38,11 @@ module Gitlab
gon.current_user_fullname = current_user.name
gon.current_user_avatar_url = current_user.avatar_url
end
+
+ # Flag controls a GFM feature used across many routes.
+ # Pushing the flag from one place simplifies control
+ # and facilitates easy removal.
+ push_frontend_feature_flag(:gfm_embedded_metrics)
end
# Exposes the state of a feature flag to the frontend code.
diff --git a/lib/gitlab/graphql.rb b/lib/gitlab/graphql.rb
index 8a59e83974f..74c04e5380e 100644
--- a/lib/gitlab/graphql.rb
+++ b/lib/gitlab/graphql.rb
@@ -3,9 +3,5 @@
module Gitlab
module Graphql
StandardGraphqlError = Class.new(StandardError)
-
- def self.enabled?
- Feature.enabled?(:graphql, default_enabled: true)
- end
end
end
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index f8d0208e275..e83b567308b 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -8,7 +8,7 @@ module Gitlab
extend ActiveSupport::Concern
def self.use(schema_definition)
- schema_definition.instrument(:field, Instrumentation.new, after_built_ins: true)
+ schema_definition.instrument(:field, Gitlab::Graphql::Authorize::Instrumentation.new, after_built_ins: true)
end
end
end
diff --git a/lib/gitlab/graphql/calls_gitaly.rb b/lib/gitlab/graphql/calls_gitaly.rb
new file mode 100644
index 00000000000..40cd74a34f2
--- /dev/null
+++ b/lib/gitlab/graphql/calls_gitaly.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ # Wraps the field resolution to count Gitaly calls before and after.
+ # Raises an error if the field calls Gitaly but hadn't declared such.
+ module CallsGitaly
+ extend ActiveSupport::Concern
+
+ def self.use(schema_definition)
+ schema_definition.instrument(:field, Gitlab::Graphql::CallsGitaly::Instrumentation.new, after_built_ins: true)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/calls_gitaly/instrumentation.rb b/lib/gitlab/graphql/calls_gitaly/instrumentation.rb
new file mode 100644
index 00000000000..fbd5e348c7d
--- /dev/null
+++ b/lib/gitlab/graphql/calls_gitaly/instrumentation.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module CallsGitaly
+ class Instrumentation
+ # Check if any `calls_gitaly: true` declarations need to be added
+ # Do nothing if a constant complexity was provided
+ def instrument(_type, field)
+ type_object = field.metadata[:type_class]
+ return field unless type_object.respond_to?(:calls_gitaly?)
+ return field if type_object.constant_complexity? || type_object.calls_gitaly?
+
+ old_resolver_proc = field.resolve_proc
+
+ gitaly_wrapped_resolve = -> (typed_object, args, ctx) do
+ previous_gitaly_call_count = Gitlab::GitalyClient.get_request_count
+ result = old_resolver_proc.call(typed_object, args, ctx)
+ current_gitaly_call_count = Gitlab::GitalyClient.get_request_count
+ calls_gitaly_check(type_object, current_gitaly_call_count - previous_gitaly_call_count)
+ result
+ end
+
+ field.redefine do
+ resolve(gitaly_wrapped_resolve)
+ end
+ end
+
+ def calls_gitaly_check(type_object, calls)
+ return if calls < 1
+
+ # Will inform you if there needs to be `calls_gitaly: true` as a kwarg in the field declaration
+ # if there is at least 1 Gitaly call involved with the field resolution.
+ error = RuntimeError.new("Gitaly is called for field '#{type_object.name}' on #{type_object.owner.try(:name)} - please either specify a constant complexity or add `calls_gitaly: true` to the field declaration")
+ Gitlab::Sentry.track_exception(error)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb
index 9048967d4e1..b10e963170a 100644
--- a/lib/gitlab/graphql/mount_mutation.rb
+++ b/lib/gitlab/graphql/mount_mutation.rb
@@ -6,11 +6,12 @@ module Gitlab
extend ActiveSupport::Concern
class_methods do
- def mount_mutation(mutation_class)
+ def mount_mutation(mutation_class, **custom_kwargs)
# Using an underscored field name symbol will make `graphql-ruby`
# standardize the field name
field mutation_class.graphql_name.underscore.to_sym,
- mutation: mutation_class
+ mutation: mutation_class,
+ **custom_kwargs
end
end
end
diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb
index db2b4dde244..58bce613a98 100644
--- a/lib/gitlab/http.rb
+++ b/lib/gitlab/http.rb
@@ -10,9 +10,9 @@ module Gitlab
RedirectionTooDeep = Class.new(StandardError)
HTTP_ERRORS = [
- SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET,
- Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::OpenTimeout,
- Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError,
+ SocketError, OpenSSL::SSL::SSLError, OpenSSL::OpenSSLError,
+ Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
+ Net::OpenTimeout, Net::ReadTimeout, Gitlab::HTTP::BlockedUrlError,
Gitlab::HTTP::RedirectionTooDeep
].freeze
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index a0fb051e806..01437c67fa9 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -160,6 +160,7 @@ excluded_attributes:
- :milestone_id
- :ref_fetched
- :merge_jid
+ - :rebase_jid
- :latest_merge_request_diff_id
award_emoji:
- :awardable_id
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index 17eacbd21d8..eef802caabb 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -6,6 +6,12 @@ module Gitlab
module Metrics
module Samplers
class RubySampler < BaseSampler
+ def initialize(interval)
+ metrics[:process_start_time_seconds].set(labels.merge(worker_label), Time.now.to_i)
+
+ super
+ end
+
def metrics
@metrics ||= init_metrics
end
@@ -47,7 +53,6 @@ module Gitlab
metrics[:file_descriptors].set(labels.merge(worker_label), System.file_descriptor_count)
metrics[:process_cpu_seconds_total].set(labels.merge(worker_label), ::Gitlab::Metrics::System.cpu_time)
metrics[:process_max_fds].set(labels.merge(worker_label), ::Gitlab::Metrics::System.max_open_file_descriptors)
- metrics[:process_start_time_seconds].set(labels.merge(worker_label), ::Gitlab::Metrics::System.process_start_time)
set_memory_usage_metrics
sample_gc
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index 34de40ca72f..5c2f07b95e2 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -31,14 +31,6 @@ module Gitlab
match[1].to_i
end
-
- def self.process_start_time
- fields = File.read('/proc/self/stat').split
-
- # fields[21] is linux proc stat field "(22) starttime".
- # The value is expressed in clock ticks, divide by clock ticks for seconds.
- ( fields[21].to_i || 0 ) / clk_tck
- end
else
def self.memory_usage
0.0
@@ -51,10 +43,6 @@ module Gitlab
def self.max_open_file_descriptors
0
end
-
- def self.process_start_time
- 0
- end
end
def self.cpu_time
diff --git a/lib/gitlab/namespaced_session_store.rb b/lib/gitlab/namespaced_session_store.rb
index 34520078bfb..f0f24c081c3 100644
--- a/lib/gitlab/namespaced_session_store.rb
+++ b/lib/gitlab/namespaced_session_store.rb
@@ -4,19 +4,24 @@ module Gitlab
class NamespacedSessionStore
delegate :[], :[]=, to: :store
- def initialize(key)
+ def initialize(key, session = Session.current)
@key = key
+ @session = session
end
def initiated?
- !Session.current.nil?
+ !session.nil?
end
def store
- return unless Session.current
+ return unless session
- Session.current[@key] ||= {}
- Session.current[@key]
+ session[@key] ||= {}
+ session[@key]
end
+
+ private
+
+ attr_reader :session
end
end
diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb
index 4b0c7b5c7f8..07439d8e011 100644
--- a/lib/gitlab/performance_bar.rb
+++ b/lib/gitlab/performance_bar.rb
@@ -3,7 +3,8 @@
module Gitlab
module PerformanceBar
ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze
- EXPIRY_TIME = 5.minutes
+ EXPIRY_TIME_L1_CACHE = 1.minute
+ EXPIRY_TIME_L2_CACHE = 5.minutes
def self.enabled?(user = nil)
return true if Rails.env.development?
@@ -19,20 +20,31 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def self.allowed_user_ids
- Rails.cache.fetch(ALLOWED_USER_IDS_KEY, expires_in: EXPIRY_TIME) do
- group = Group.find_by_id(allowed_group_id)
+ l1_cache_backend.fetch(ALLOWED_USER_IDS_KEY, expires_in: EXPIRY_TIME_L1_CACHE) do
+ l2_cache_backend.fetch(ALLOWED_USER_IDS_KEY, expires_in: EXPIRY_TIME_L2_CACHE) do
+ group = Group.find_by_id(allowed_group_id)
- if group
- GroupMembersFinder.new(group).execute.pluck(:user_id)
- else
- []
+ if group
+ GroupMembersFinder.new(group).execute.pluck(:user_id)
+ else
+ []
+ end
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def self.expire_allowed_user_ids_cache
- Rails.cache.delete(ALLOWED_USER_IDS_KEY)
+ l1_cache_backend.delete(ALLOWED_USER_IDS_KEY)
+ l2_cache_backend.delete(ALLOWED_USER_IDS_KEY)
+ end
+
+ def self.l1_cache_backend
+ Gitlab::ThreadMemoryCache.cache_backend
+ end
+
+ def self.l2_cache_backend
+ Rails.cache
end
end
end
diff --git a/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb b/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb
new file mode 100644
index 00000000000..2d997760c46
--- /dev/null
+++ b/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# Adapted from https://github.com/peek/peek/blob/master/lib/peek/adapters/redis.rb
+module Gitlab
+ module PerformanceBar
+ module RedisAdapterWhenPeekEnabled
+ def save
+ super unless ::Peek.request_id.blank?
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/quick_actions/issuable_actions.rb b/lib/gitlab/quick_actions/issuable_actions.rb
index 572c55efcc2..f7f89d4e897 100644
--- a/lib/gitlab/quick_actions/issuable_actions.rb
+++ b/lib/gitlab/quick_actions/issuable_actions.rb
@@ -146,8 +146,8 @@ module Gitlab
@updates[:todo_event] = 'add'
end
- desc _('Mark todo as done')
- explanation _('Marks todo as done.')
+ desc _('Mark to do as done')
+ explanation _('Marks to do as done.')
types Issuable
condition do
quick_action_target.persisted? &&
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
index 583a970bf4e..0f890a12134 100644
--- a/lib/gitlab/sidekiq_status.rb
+++ b/lib/gitlab/sidekiq_status.rb
@@ -53,14 +53,14 @@ module Gitlab
self.num_running(job_ids).zero?
end
- # Returns true if the given job is running
+ # Returns true if the given job is running or enqueued.
#
# job_id - The Sidekiq job ID to check.
def self.running?(job_id)
num_running([job_id]) > 0
end
- # Returns the number of jobs that are running.
+ # Returns the number of jobs that are running or enqueued.
#
# job_ids - The Sidekiq job IDs to check.
def self.num_running(job_ids)
@@ -81,7 +81,7 @@ module Gitlab
# job_ids - The Sidekiq job IDs to check.
#
# Returns an array of true or false indicating job completion.
- # true = job is still running
+ # true = job is still running or enqueued
# false = job completed
def self.job_status(job_ids)
keys = job_ids.map { |jid| key_for(jid) }
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index fd108b4c124..f6edbfced7f 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -9,14 +9,16 @@ module Gitlab
REGEX_QUOTED_WORD = /(?<=\A| )"[^"]+"(?= |\z)/.freeze
class_methods do
- def fuzzy_search(query, columns)
- matches = columns.map { |col| fuzzy_arel_match(col, query) }.compact.reduce(:or)
+ def fuzzy_search(query, columns, use_minimum_char_limit: true)
+ matches = columns.map do |col|
+ fuzzy_arel_match(col, query, use_minimum_char_limit: use_minimum_char_limit)
+ end.compact.reduce(:or)
where(matches)
end
- def to_pattern(query)
- if partial_matching?(query)
+ def to_pattern(query, use_minimum_char_limit: true)
+ if partial_matching?(query, use_minimum_char_limit: use_minimum_char_limit)
"%#{sanitize_sql_like(query)}%"
else
sanitize_sql_like(query)
@@ -27,7 +29,9 @@ module Gitlab
MIN_CHARS_FOR_PARTIAL_MATCHING
end
- def partial_matching?(query)
+ def partial_matching?(query, use_minimum_char_limit: true)
+ return true unless use_minimum_char_limit
+
query.length >= min_chars_for_partial_matching
end
@@ -35,14 +39,14 @@ module Gitlab
# query - The text to search for.
# lower_exact_match - When set to `true` we'll fall back to using
# `LOWER(column) = query` instead of using `ILIKE`.
- def fuzzy_arel_match(column, query, lower_exact_match: false)
+ def fuzzy_arel_match(column, query, lower_exact_match: false, use_minimum_char_limit: true)
query = query.squish
return unless query.present?
- words = select_fuzzy_words(query)
+ words = select_fuzzy_words(query, use_minimum_char_limit: use_minimum_char_limit)
if words.any?
- words.map { |word| arel_table[column].matches(to_pattern(word)) }.reduce(:and)
+ words.map { |word| arel_table[column].matches(to_pattern(word, use_minimum_char_limit: use_minimum_char_limit)) }.reduce(:and)
else
# No words of at least 3 chars, but we can search for an exact
# case insensitive match with the query as a whole
@@ -56,7 +60,7 @@ module Gitlab
end
end
- def select_fuzzy_words(query)
+ def select_fuzzy_words(query, use_minimum_char_limit: true)
quoted_words = query.scan(REGEX_QUOTED_WORD)
query = quoted_words.reduce(query) { |q, quoted_word| q.sub(quoted_word, '') }
@@ -67,7 +71,7 @@ module Gitlab
words.concat(quoted_words)
- words.select { |word| partial_matching?(word) }
+ words.select { |word| partial_matching?(word, use_minimum_char_limit: use_minimum_char_limit) }
end
end
end
diff --git a/lib/gitlab/user_extractor.rb b/lib/gitlab/user_extractor.rb
deleted file mode 100644
index ede60c9ab1d..00000000000
--- a/lib/gitlab/user_extractor.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# frozen_string_literal: true
-
-# This class extracts all users found in a piece of text by the username or the
-# email address
-
-module Gitlab
- class UserExtractor
- # Not using `Devise.email_regexp` to filter out any chars that an email
- # does not end with and not pinning the email to a start of end of a string.
- EMAIL_REGEXP = /(?<email>([^@\s]+@[^@\s]+(?<!\W)))/.freeze
- USERNAME_REGEXP = User.reference_pattern
-
- def initialize(text)
- # EE passes an Array to `text` in a few places, so we want to support both
- # here.
- @text = Array(text).join(' ')
- end
-
- def users
- return User.none unless @text.present?
- return User.none if references.empty?
-
- @users ||= User.from_union(union_relations)
- end
-
- def usernames
- matches[:usernames]
- end
-
- def emails
- matches[:emails]
- end
-
- def references
- @references ||= matches.values.flatten
- end
-
- def matches
- @matches ||= {
- emails: @text.scan(EMAIL_REGEXP).flatten.uniq,
- usernames: @text.scan(USERNAME_REGEXP).flatten.uniq
- }
- end
-
- private
-
- def union_relations
- relations = []
-
- relations << User.by_any_email(emails) if emails.any?
- relations << User.by_username(usernames) if usernames.any?
-
- relations
- end
- end
-end
diff --git a/lib/peek/views/redis.rb b/lib/peek/views/redis.rb
index ad3c3c9fe01..73de8672fa4 100644
--- a/lib/peek/views/redis.rb
+++ b/lib/peek/views/redis.rb
@@ -37,6 +37,8 @@ end
module Peek
module Views
module RedisDetailed
+ REDACTED_MARKER = "<redacted>"
+
def results
super.merge(details: details)
end
@@ -57,10 +59,12 @@ module Peek
end
def format_command(cmd)
+ if cmd.length >= 2 && cmd.first =~ /^auth$/i
+ cmd[-1] = REDACTED_MARKER
# Scrub out the value of the SET calls to avoid binary
# data or large data from spilling into the view
- if cmd.length >= 2 && cmd.first =~ /set/i
- cmd[-1] = "<redacted>"
+ elsif cmd.length >= 3 && cmd.first =~ /set/i
+ cmd[2..-1] = REDACTED_MARKER
end
cmd.join(' ')
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0738df663fb..9b6e8d8c8a4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -28,6 +28,12 @@ msgstr ""
msgid " You need to do this before %{grace_period_deadline}."
msgstr ""
+msgid " and "
+msgstr ""
+
+msgid " and %{sliced}"
+msgstr ""
+
msgid " or "
msgstr ""
@@ -112,10 +118,10 @@ msgstr[1] ""
msgid "%{actionText} & %{openOrClose} %{noteable}"
msgstr ""
-msgid "%{commit_author_link} authored %{commit_timeago}"
+msgid "%{canMergeCount}/%{assigneesCount} can merge"
msgstr ""
-msgid "%{counter_repositories} repositories, %{counter_wikis} wikis, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS"
+msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
msgid "%{count} more"
@@ -147,6 +153,9 @@ msgstr ""
msgid "%{group_docs_link_start}Groups%{group_docs_link_end} allow you to manage and collaborate across multiple projects. Members of a group have access to all of its projects."
msgstr ""
+msgid "%{icon}You are about to add %{usersTag} people to the discussion. Proceed with caution."
+msgstr ""
+
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
@@ -168,6 +177,9 @@ msgstr ""
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
+msgid "%{listToShow}, and %{awardsListLength} more."
+msgstr ""
+
msgid "%{loadingIcon} Started"
msgstr ""
@@ -251,6 +263,9 @@ msgstr ""
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgstr ""
+msgid "%{userName}'s avatar"
+msgstr ""
+
msgid "%{user_name} profile page"
msgstr ""
@@ -275,6 +290,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+ %{numberOfHiddenAssignees} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -605,9 +623,18 @@ msgstr ""
msgid "Add a GPG key"
msgstr ""
+msgid "Add a Grafana button in the admin sidebar, monitoring section, to access a variety of statistics on the health and performance of GitLab."
+msgstr ""
+
+msgid "Add a To Do"
+msgstr ""
+
msgid "Add a bullet list"
msgstr ""
+msgid "Add a general comment to this %{noteableDisplayName}."
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
@@ -680,9 +707,6 @@ msgstr ""
msgid "Add to review"
msgstr ""
-msgid "Add todo"
-msgstr ""
-
msgid "Add user(s) to the group:"
msgstr ""
@@ -869,9 +893,6 @@ msgstr ""
msgid "All projects"
msgstr ""
-msgid "All todos were marked as done."
-msgstr ""
-
msgid "All users"
msgstr ""
@@ -1199,6 +1220,9 @@ msgstr ""
msgid "Are you sure you want to cancel editing this comment?"
msgstr ""
+msgid "Are you sure you want to delete this %{typeOfComment}?"
+msgstr ""
+
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
@@ -1317,7 +1341,9 @@ msgid "Assigned to me"
msgstr ""
msgid "Assignee"
-msgstr ""
+msgid_plural "%d Assignees"
+msgstr[0] ""
+msgstr[1] ""
msgid "Assignee(s)"
msgstr ""
@@ -1879,6 +1905,9 @@ msgstr ""
msgid "Cancel this job"
msgstr ""
+msgid "Cancelling Preview"
+msgstr ""
+
msgid "Cannot be merged automatically"
msgstr ""
@@ -1888,6 +1917,9 @@ msgstr ""
msgid "Cannot create the abuse report. This user has been blocked."
msgstr ""
+msgid "Cannot merge"
+msgstr ""
+
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
@@ -3617,6 +3649,9 @@ msgstr ""
msgid "Deployed"
msgstr ""
+msgid "Deployed %{deployedSince}"
+msgstr ""
+
msgid "Deployed to"
msgstr ""
@@ -3719,6 +3754,12 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discuss a specific suggestion or question that needs to be resolved."
+msgstr ""
+
+msgid "Discuss a specific suggestion or question."
+msgstr ""
+
msgid "Discussion"
msgstr ""
@@ -3761,6 +3802,9 @@ msgstr ""
msgid "Download artifacts"
msgstr ""
+msgid "Download as"
+msgstr ""
+
msgid "Download asset"
msgstr ""
@@ -3923,9 +3967,15 @@ msgstr ""
msgid "Enable HTML emails"
msgstr ""
+msgid "Enable access to Grafana"
+msgstr ""
+
msgid "Enable access to the Performance Bar for a given group."
msgstr ""
+msgid "Enable and configure Grafana."
+msgstr ""
+
msgid "Enable and configure InfluxDB metrics."
msgstr ""
@@ -3986,6 +4036,9 @@ msgstr ""
msgid "Enforce DNS rebinding attack protection"
msgstr ""
+msgid "Ensure connectivity is available from the GitLab server to the Prometheus server"
+msgstr ""
+
msgid "Enter at least three characters to search"
msgstr ""
@@ -4007,6 +4060,9 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
+msgid "Enter zen mode"
+msgstr ""
+
msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they can be masked so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -4160,6 +4216,9 @@ msgstr ""
msgid "Error fetching diverging counts for branches. Please try again."
msgstr ""
+msgid "Error fetching forked projects. Please try again."
+msgstr ""
+
msgid "Error fetching labels."
msgstr ""
@@ -4211,6 +4270,9 @@ msgstr ""
msgid "Error occurred when fetching sidebar data"
msgstr ""
+msgid "Error occurred when saving assignees"
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
@@ -4238,10 +4300,10 @@ msgstr ""
msgid "Error updating %{issuableType}"
msgstr ""
-msgid "Error updating status for all todos."
+msgid "Error updating status for all to-do items."
msgstr ""
-msgid "Error updating todo status."
+msgid "Error updating status of to-do item."
msgstr ""
msgid "Error uploading file"
@@ -4340,6 +4402,9 @@ msgstr ""
msgid "Everyone can contribute"
msgstr ""
+msgid "Everything on your to-do list is marked as done."
+msgstr ""
+
msgid "Everything you need to create a GitLab Pages site using GitBook."
msgstr ""
@@ -4367,6 +4432,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand dropdown"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -4562,6 +4630,9 @@ msgstr ""
msgid "Failure"
msgstr ""
+msgid "Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged."
+msgstr ""
+
msgid "Fast-forward merge without a merge commit"
msgstr ""
@@ -4744,6 +4815,9 @@ msgstr ""
msgid "Friday"
msgstr ""
+msgid "From"
+msgstr ""
+
msgid "From %{providerTitle}"
msgstr ""
@@ -4933,6 +5007,9 @@ msgstr ""
msgid "Got it!"
msgstr ""
+msgid "Grafana URL"
+msgstr ""
+
msgid "Grant access"
msgstr ""
@@ -5313,6 +5390,12 @@ msgstr ""
msgid "ImageDiffViewer|Swipe"
msgstr ""
+msgid "ImageViewerDimensions|H"
+msgstr ""
+
+msgid "ImageViewerDimensions|W"
+msgstr ""
+
msgid "Impersonation has been disabled"
msgstr ""
@@ -6196,6 +6279,9 @@ msgstr ""
msgid "March"
msgstr ""
+msgid "Mark as done"
+msgstr ""
+
msgid "Mark as resolved"
msgstr ""
@@ -6205,7 +6291,7 @@ msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
-msgid "Mark todo as done"
+msgid "Mark to do as done"
msgstr ""
msgid "Markdown"
@@ -6217,10 +6303,13 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
+msgid "Markdown is supported"
+msgstr ""
+
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr ""
-msgid "Marks todo as done."
+msgid "Marks to do as done."
msgstr ""
msgid "Max access level"
@@ -6391,12 +6480,18 @@ msgstr ""
msgid "Metrics"
msgstr ""
+msgid "Metrics - Grafana"
+msgstr ""
+
msgid "Metrics - Influx"
msgstr ""
msgid "Metrics - Prometheus"
msgstr ""
+msgid "Metrics Dashboard"
+msgstr ""
+
msgid "Metrics and profiling"
msgstr ""
@@ -6807,6 +6902,9 @@ msgstr ""
msgid "No files found."
msgstr ""
+msgid "No forks available to you."
+msgstr ""
+
msgid "No job trace"
msgstr ""
@@ -6825,6 +6923,9 @@ msgstr ""
msgid "No milestones to show"
msgstr ""
+msgid "No one can merge"
+msgstr ""
+
msgid "No other labels with such name or description"
msgstr ""
@@ -6885,6 +6986,9 @@ msgstr ""
msgid "Not started"
msgstr ""
+msgid "Note"
+msgstr ""
+
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr ""
@@ -7286,6 +7390,12 @@ msgstr ""
msgid "Pipeline"
msgstr ""
+msgid "Pipeline %{label}"
+msgstr ""
+
+msgid "Pipeline %{label} for \"%{dataTitle}\""
+msgstr ""
+
msgid "Pipeline Schedule"
msgstr ""
@@ -7511,6 +7621,9 @@ msgstr ""
msgid "Please %{link_to_register} or %{link_to_sign_in} to comment"
msgstr ""
+msgid "Please %{startTagRegister}register%{endRegisterTag} or %{startTagSignIn}sign in%{endSignInTag} to reply"
+msgstr ""
+
msgid "Please accept the Terms of Service before continuing."
msgstr ""
@@ -8312,9 +8425,6 @@ msgstr ""
msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr ""
-msgid "Prometheus server"
-msgstr ""
-
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr ""
@@ -8510,6 +8620,12 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Rebase"
+msgstr ""
+
+msgid "Rebase in progress"
+msgstr ""
+
msgid "Receive notifications about your own activity"
msgstr ""
@@ -8758,6 +8874,9 @@ msgstr ""
msgid "Repository storage"
msgstr ""
+msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}"
+msgstr ""
+
msgid "Request Access"
msgstr ""
@@ -9174,6 +9293,12 @@ msgstr ""
msgid "Select members to invite"
msgstr ""
+msgid "Select merge moment"
+msgstr ""
+
+msgid "Select private project"
+msgstr ""
+
msgid "Select project"
msgstr ""
@@ -9575,6 +9700,9 @@ msgstr ""
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
+msgid "Something went wrong while adding your comment. Please try again."
+msgstr ""
+
msgid "Something went wrong while applying the suggestion. Please try again."
msgstr ""
@@ -9584,6 +9712,12 @@ msgstr ""
msgid "Something went wrong while deleting the source branch. Please try again."
msgstr ""
+msgid "Something went wrong while deleting your note. Please try again."
+msgstr ""
+
+msgid "Something went wrong while editing your comment. Please try again."
+msgstr ""
+
msgid "Something went wrong while fetching comments. Please try again."
msgstr ""
@@ -9611,6 +9745,9 @@ msgstr ""
msgid "Something went wrong while resolving this discussion. Please try again."
msgstr ""
+msgid "Something went wrong while stopping this environment. Please try again."
+msgstr ""
+
msgid "Something went wrong, unable to search projects"
msgstr ""
@@ -10600,6 +10737,9 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This comment has changed since you started editing, please review the %{startTag}updated comment%{endTag} to ensure information is not lost."
+msgstr ""
+
msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
msgstr ""
@@ -10651,6 +10791,9 @@ msgstr ""
msgid "This is a \"Ghost User\", created to hold all issues authored by users that have since been deleted. This user cannot be removed."
msgstr ""
+msgid "This is a Work in Progress"
+msgstr ""
+
msgid "This is a confidential issue."
msgstr ""
@@ -10669,6 +10812,9 @@ msgstr ""
msgid "This is your current session"
msgstr ""
+msgid "This issue is %{confidentialLinkStart}confidential%{linkEnd} and %{lockedLinkStart}locked%{linkEnd}."
+msgstr ""
+
msgid "This issue is confidential"
msgstr ""
@@ -10735,6 +10881,9 @@ msgstr ""
msgid "This job will automatically run after its timer finishes. Often they are used for incremental roll-out deploys to production environments. When unscheduled it converts into a manual action."
msgstr ""
+msgid "This may expose confidential information as the selected fork is in another namespace that can have other members."
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
@@ -10849,12 +10998,21 @@ msgstr ""
msgid "TimeTrackingEstimated|Est"
msgstr ""
+msgid "TimeTracking|%{startTag}Spent: %{endTag}%{timeSpentHumanReadable}"
+msgstr ""
+
msgid "TimeTracking|Estimated:"
msgstr ""
+msgid "TimeTracking|Over by %{timeRemainingHumanReadable}"
+msgstr ""
+
msgid "TimeTracking|Spent"
msgstr ""
+msgid "TimeTracking|Time remaining: %{timeRemainingHumanReadable}"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr ""
@@ -11066,6 +11224,12 @@ msgstr ""
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
msgstr ""
+msgid "To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private."
+msgstr ""
+
+msgid "To protect this issue's confidentiality, a private fork of this project was selected."
+msgstr ""
+
msgid "To see all the user's personal access tokens you must impersonate them first."
msgstr ""
@@ -11087,16 +11251,13 @@ msgstr ""
msgid "To widen your search, change or remove filters above"
msgstr ""
-msgid "Today"
+msgid "To-Do List"
msgstr ""
-msgid "Todo"
+msgid "To-do item successfully marked as done."
msgstr ""
-msgid "Todo was successfully marked as done."
-msgstr ""
-
-msgid "Todos"
+msgid "Today"
msgstr ""
msgid "Toggle Sidebar"
@@ -11120,6 +11281,9 @@ msgstr ""
msgid "Toggle navigation"
msgstr ""
+msgid "Toggle sidebar"
+msgstr ""
+
msgid "Toggle thread"
msgstr ""
@@ -11237,6 +11401,12 @@ msgstr ""
msgid "Tuesday"
msgstr ""
+msgid "Turn Off"
+msgstr ""
+
+msgid "Turn On"
+msgstr ""
+
msgid "Twitter"
msgstr ""
@@ -11264,6 +11434,9 @@ msgstr ""
msgid "U2F only works with HTTPS-enabled websites. Contact your administrator for more details."
msgstr ""
+msgid "Unable to apply suggestions to a deleted line."
+msgstr ""
+
msgid "Unable to connect to Prometheus server"
msgstr ""
@@ -11372,6 +11545,9 @@ msgstr ""
msgid "Unsubscribe from %{type}"
msgstr ""
+msgid "Until"
+msgstr ""
+
msgid "Unverified"
msgstr ""
@@ -12334,7 +12510,7 @@ msgstr ""
msgid "Your SSH keys (%{count})"
msgstr ""
-msgid "Your Todos"
+msgid "Your To-Do List"
msgstr ""
msgid "Your U2F device did not send a valid JSON response."
@@ -12436,6 +12612,9 @@ msgstr ""
msgid "among other things"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "attach a new file"
msgstr ""
@@ -12445,6 +12624,9 @@ msgstr ""
msgid "branch name"
msgstr ""
+msgid "by"
+msgstr ""
+
msgid "cannot be changed if a personal project has container registry tags."
msgstr ""
@@ -12454,6 +12636,9 @@ msgstr ""
msgid "cannot include leading slash or directory traversal."
msgstr ""
+msgid "comment"
+msgstr ""
+
msgid "commented on %{link_to_project}"
msgstr ""
@@ -12472,6 +12657,9 @@ msgstr ""
msgid "could not read private key, is the passphrase correct?"
msgstr ""
+msgid "created"
+msgstr ""
+
msgid "customize"
msgstr ""
@@ -12579,6 +12767,9 @@ msgstr ""
msgid "is not an email you own"
msgstr ""
+msgid "issue"
+msgstr ""
+
msgid "issue boards"
msgstr ""
@@ -12629,6 +12820,15 @@ msgstr ""
msgid "mrWidgetCommitsAdded|1 merge commit"
msgstr ""
+msgid "mrWidgetNothingToMerge|Currently there are no changes in this merge request's source branch. Please push new commits or use a different branch."
+msgstr ""
+
+msgid "mrWidgetNothingToMerge|Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
+msgid "mrWidgetNothingToMerge|Merge requests are a place to propose changes you have made to a project and discuss those changes with others."
+msgstr ""
+
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
msgstr ""
@@ -12746,6 +12946,9 @@ msgstr ""
msgid "mrWidget|Request to merge"
msgstr ""
+msgid "mrWidget|Resolve WIP status"
+msgstr ""
+
msgid "mrWidget|Resolve conflicts"
msgstr ""
@@ -12809,6 +13012,9 @@ msgstr ""
msgid "mrWidget|This project is archived, write access has been disabled"
msgstr ""
+msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
+msgstr ""
+
msgid "mrWidget|You are not allowed to edit this project directly. Please fork to make changes."
msgstr ""
@@ -12879,6 +13085,9 @@ msgstr[1] ""
msgid "password"
msgstr ""
+msgid "pending comment"
+msgstr ""
+
msgid "private"
msgstr ""
@@ -12891,6 +13100,9 @@ msgstr ""
msgid "project"
msgstr ""
+msgid "project avatar"
+msgstr ""
+
msgid "quick actions"
msgstr ""
@@ -12903,6 +13115,9 @@ msgstr ""
msgid "remaining"
msgstr ""
+msgid "remove"
+msgstr ""
+
msgid "remove due date"
msgstr ""
@@ -12973,12 +13188,21 @@ msgstr[1] ""
msgid "to list"
msgstr ""
+msgid "toggle collapse"
+msgstr ""
+
+msgid "toggle dropdown"
+msgstr ""
+
msgid "triggered"
msgstr ""
msgid "updated"
msgstr ""
+msgid "user avatar"
+msgstr ""
+
msgid "username"
msgstr ""
diff --git a/package.json b/package.json
index e645eb8ed1c..5955790488a 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,8 @@
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
- "@gitlab/svgs": "^1.66.0",
- "@gitlab/ui": "^5.1.0",
+ "@gitlab/svgs": "^1.67.0",
+ "@gitlab/ui": "^5.5.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-link": "^1.2.11",
diff --git a/qa/.rspec_parallel b/qa/.rspec_parallel
new file mode 100644
index 00000000000..e5927927eaa
--- /dev/null
+++ b/qa/.rspec_parallel
@@ -0,0 +1,5 @@
+--color
+--format documentation
+--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
+--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
+--require spec_helper
diff --git a/qa/Gemfile b/qa/Gemfile
index 12994b85322..c46be8a0362 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -1,5 +1,6 @@
source 'https://rubygems.org'
+gem 'gitlab-qa'
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'capybara', '~> 2.16.1'
gem 'capybara-screenshot', '~> 1.0.18'
@@ -11,3 +12,4 @@ gem 'nokogiri', '~> 1.10.3'
gem 'rspec-retry', '~> 0.6.1'
gem 'faker', '~> 1.6', '>= 1.6.6'
gem 'knapsack', '~> 1.17'
+gem 'parallel_tests', '~> 2.29'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 6b0635ed0e2..73aabf2c6ad 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -35,6 +35,7 @@ GEM
faker (1.9.3)
i18n (>= 0.7)
ffi (1.9.25)
+ gitlab-qa (4.0.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (0.9.1)
@@ -53,6 +54,9 @@ GEM
netrc (0.11.0)
nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
+ parallel (1.17.0)
+ parallel_tests (2.29.0)
+ parallel
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
@@ -104,8 +108,10 @@ DEPENDENCIES
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
faker (~> 1.6, >= 1.6.6)
+ gitlab-qa
knapsack (~> 1.17)
nokogiri (~> 1.10.3)
+ parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
diff --git a/qa/qa.rb b/qa/qa.rb
index 10d44b6f6d9..be73776425b 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -360,6 +360,7 @@ module QA
module Specs
autoload :Config, 'qa/specs/config'
autoload :Runner, 'qa/specs/runner'
+ autoload :ParallelRunner, 'qa/specs/parallel_runner'
module Helpers
autoload :Quarantine, 'qa/specs/helpers/quarantine'
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 1a9a2fd413f..9fd668f812b 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -5,6 +5,7 @@ module QA
module Project
class Show < Page::Base
include Page::Component::ClonePanel
+ include Page::Project::SubMenus::Settings
view 'app/views/layouts/header/_new_dropdown.haml' do
element :new_menu_toggle
diff --git a/qa/qa/page/project/sub_menus/ci_cd.rb b/qa/qa/page/project/sub_menus/ci_cd.rb
index adae2ce08c4..2f0bc8b9ba6 100644
--- a/qa/qa/page/project/sub_menus/ci_cd.rb
+++ b/qa/qa/page/project/sub_menus/ci_cd.rb
@@ -5,6 +5,8 @@ module QA
module Project
module SubMenus
module CiCd
+ include Page::Project::SubMenus::Common
+
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
diff --git a/qa/qa/page/project/sub_menus/issues.rb b/qa/qa/page/project/sub_menus/issues.rb
index f81e4f34909..8fb8fa06346 100644
--- a/qa/qa/page/project/sub_menus/issues.rb
+++ b/qa/qa/page/project/sub_menus/issues.rb
@@ -5,6 +5,8 @@ module QA
module Project
module SubMenus
module Issues
+ include Page::Project::SubMenus::Common
+
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
diff --git a/qa/qa/page/project/sub_menus/operations.rb b/qa/qa/page/project/sub_menus/operations.rb
index 24a99a9464c..d266cb21417 100644
--- a/qa/qa/page/project/sub_menus/operations.rb
+++ b/qa/qa/page/project/sub_menus/operations.rb
@@ -5,6 +5,8 @@ module QA
module Project
module SubMenus
module Operations
+ include Page::Project::SubMenus::Common
+
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
diff --git a/qa/qa/page/project/sub_menus/repository.rb b/qa/qa/page/project/sub_menus/repository.rb
index 4cc73a6b25a..c53d805c61d 100644
--- a/qa/qa/page/project/sub_menus/repository.rb
+++ b/qa/qa/page/project/sub_menus/repository.rb
@@ -5,6 +5,8 @@ module QA
module Project
module SubMenus
module Repository
+ include Page::Project::SubMenus::Common
+
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb
index 88b45ec55ae..1cd39fcff58 100644
--- a/qa/qa/page/project/sub_menus/settings.rb
+++ b/qa/qa/page/project/sub_menus/settings.rb
@@ -5,6 +5,8 @@ module QA
module Project
module SubMenus
module Settings
+ include Page::Project::SubMenus::Common
+
def self.included(base)
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
diff --git a/qa/qa/resource/issue.rb b/qa/qa/resource/issue.rb
index 9c57a0f5afb..51b2af8b4ef 100644
--- a/qa/qa/resource/issue.rb
+++ b/qa/qa/resource/issue.rb
@@ -16,6 +16,10 @@ module QA
attribute :labels
attribute :title
+ def initialize
+ @labels = []
+ end
+
def fabricate!
project.visit!
@@ -38,7 +42,7 @@ module QA
def api_post_body
{
- labels: [labels],
+ labels: labels,
title: title
}
end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index ed0779b93cc..2987bb1a213 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -13,6 +13,8 @@ module QA
NotRespondingError = Class.new(RuntimeError)
+ CAPYBARA_MAX_WAIT_TIME = 10
+
def initialize
self.class.configure!
end
@@ -43,6 +45,8 @@ module QA
end
end
+ Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i
+
return if Capybara.drivers.include?(:chrome)
Capybara.register_driver QA::Runtime::Env.browser do |app|
@@ -119,7 +123,7 @@ module QA
Capybara.configure do |config|
config.default_driver = QA::Runtime::Env.browser
config.javascript_driver = QA::Runtime::Env.browser
- config.default_max_wait_time = 10
+ config.default_max_wait_time = CAPYBARA_MAX_WAIT_TIME
# https://github.com/mattheworiordan/capybara-screenshot/issues/164
config.save_path = ::File.expand_path('../../tmp', __dir__)
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 96f337dc081..d50f618ff82 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'gitlab/qa'
+
module QA
module Runtime
module Env
@@ -7,6 +9,8 @@ module QA
attr_writer :personal_access_token, :ldap_username, :ldap_password
+ ENV_VARIABLES = Gitlab::QA::Runtime::Env::ENV_VARIABLES
+
# The environment variables used to indicate if the environment under test
# supports the given feature
SUPPORTED_FEATURES = {
@@ -201,6 +205,10 @@ module QA
enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
end
+ def runtime_scenario_attributes
+ ENV['QA_RUNTIME_SCENARIO_ATTRIBUTES']
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/runtime/scenario.rb b/qa/qa/runtime/scenario.rb
index 5067322804b..3662ebe671b 100644
--- a/qa/qa/runtime/scenario.rb
+++ b/qa/qa/runtime/scenario.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'json'
+
module QA
module Runtime
##
@@ -24,6 +26,10 @@ module QA
end
end
+ def from_env(var)
+ JSON.parse(Runtime::Env.runtime_scenario_attributes).each { |k, v| define(k, v) }
+ end
+
def method_missing(name, *)
raise ArgumentError, "Scenario attribute `#{name}` not defined!"
end
diff --git a/qa/qa/scenario/shared_attributes.rb b/qa/qa/scenario/shared_attributes.rb
index 40d5c6b1ff1..52f50ec8c27 100644
--- a/qa/qa/scenario/shared_attributes.rb
+++ b/qa/qa/scenario/shared_attributes.rb
@@ -7,6 +7,7 @@ module QA
attribute :gitlab_address, '--address URL', 'Address of the instance to test'
attribute :enable_feature, '--enable-feature FEATURE_FLAG', 'Enable a feature before running tests'
+ attribute :parallel, '--parallel', 'Execute tests in parallel'
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
new file mode 100644
index 00000000000..5eceeb9661c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Plan' do
+ describe 'check xss occurence in @mentions in issues' do
+ let(:issue_title) { 'issue title' }
+
+ it 'user mentions a user in comment' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ user = Resource::User.fabricate_via_api! do |user|
+ user.name = "eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;"
+ user.password = "test1234"
+ end
+
+ project = Resource::Project.fabricate_via_api! do |resource|
+ resource.name = 'xss-test-for-mentions-project'
+ end
+ project.visit!
+
+ Page::Project::Show.perform(&:go_to_members_settings)
+ Page::Project::Settings::Members.perform do |page|
+ page.add_member(user.username)
+ end
+
+ issue = Resource::Issue.fabricate_via_api! do |issue|
+ issue.title = issue_title
+ issue.project = project
+ end
+ issue.visit!
+
+ Page::Project::Issue::Show.perform do |show_page|
+ show_page.select_all_activities_filter
+ show_page.comment('cc-ing you here @eve')
+
+ expect do
+ expect(show_page).to have_content("cc-ing you here")
+ end.not_to raise_error # Selenium::WebDriver::Error::UnhandledAlertError
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
index 4478ea41662..2101311f065 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb
@@ -9,27 +9,33 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
- Resource::Issue.fabricate_via_browser_ui! do |issue|
+ issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
end
+ issue.visit!
+
expect(page).to have_content(issue_title)
Page::Project::Issue::Show.perform do |show_page|
+ my_first_discussion = "My first discussion"
+ my_first_reply = "My First Reply"
+ one_reply = "1 reply"
+
show_page.select_all_activities_filter
- show_page.start_discussion("My first discussion")
- expect(show_page).to have_content("My first discussion")
+ show_page.start_discussion(my_first_discussion)
+ expect(show_page).to have_content(my_first_discussion)
- show_page.reply_to_discussion("My First Reply")
- expect(show_page).to have_content("My First Reply")
+ show_page.reply_to_discussion(my_first_reply)
+ expect(show_page).to have_content(my_first_reply)
show_page.collapse_replies
- expect(show_page).to have_content("1 reply")
- expect(show_page).not_to have_content("My First Reply")
+ expect(show_page).to have_content(one_reply)
+ expect(show_page).not_to have_content(my_first_reply)
show_page.expand_replies
- expect(show_page).to have_content("My First Reply")
- expect(show_page).not_to have_content("1 reply")
+ expect(show_page).to have_content(my_first_reply)
+ expect(show_page).not_to have_content(one_reply)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
index ad2773b41ac..301836f5ce8 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb
@@ -9,28 +9,33 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- Resource::Issue.fabricate_via_browser_ui! do |issue|
+ issue = Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
end
+ issue.visit!
+
expect(page).to have_content(issue_title)
Page::Project::Issue::Show.perform do |show_page|
+ my_own_comment = "My own comment"
+ made_the_issue_confidential = "made the issue confidential"
+
show_page.comment('/confidential', filter: :comments_only)
- show_page.comment('My own comment', filter: :comments_only)
+ show_page.comment(my_own_comment, filter: :comments_only)
- expect(show_page).not_to have_content("made the issue confidential")
- expect(show_page).to have_content("My own comment")
+ expect(show_page).not_to have_content(made_the_issue_confidential)
+ expect(show_page).to have_content(my_own_comment)
show_page.select_all_activities_filter
- expect(show_page).to have_content("made the issue confidential")
- expect(show_page).to have_content("My own comment")
+ expect(show_page).to have_content(made_the_issue_confidential)
+ expect(show_page).to have_content(my_own_comment)
show_page.select_history_only_filter
- expect(show_page).to have_content("made the issue confidential")
- expect(show_page).not_to have_content("My own comment")
+ expect(show_page).to have_content(made_the_issue_confidential)
+ expect(show_page).not_to have_content(my_own_comment)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
index 530fc684437..24dcb32f63f 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
@@ -14,7 +14,7 @@ module QA
resource.description = 'project for issue suggestions'
end
- Resource::Issue.fabricate_via_browser_ui! do |issue|
+ Resource::Issue.fabricate_via_api! do |issue|
issue.title = issue_title
issue.project = project
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
index db33c6330ff..9e48ee7ca2a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # https://gitlab.com/gitlab-org/quality/staging/issues/55
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Download merge request patch and diff' do
before(:context) do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index 23008a58af8..448d4980727 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # https://gitlab.com/gitlab-org/quality/staging/issues/40
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Push mirror a repository over HTTP' do
it 'configures and syncs a (push) mirrored repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 247cde38e52..11fd570d131 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create', :requires_admin do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/113
+ context 'Create', :requires_admin, :quarantine do
describe 'push after setting the file size limit via admin/application_settings' do
before(:context) do
@project = Resource::Project.fabricate_via_api! do |p|
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 5ca9ddb6b19..99f0838b864 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -9,6 +9,52 @@ module QA
Page::Main::Login.perform(&:sign_in_using_credentials)
end
+ def disable_optional_jobs(project)
+ # Disable code_quality check in Auto DevOps pipeline as it takes
+ # too long and times out the test
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'CODE_QUALITY_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'LICENSE_MANAGEMENT_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'SAST_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'DEPENDENCY_SCANNING_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'CONTAINER_SCANNING_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'DAST_DISABLED'
+ resource.value = '1'
+ resource.masked = false
+ end
+ end
+
# Failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/108
describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do
context 'when rbac is enabled' do
@@ -28,14 +74,7 @@ module QA
p.description = 'Project with Auto DevOps'
end
- # Disable code_quality check in Auto DevOps pipeline as it takes
- # too long and times out the test
- Resource::CiVariable.fabricate! do |resource|
- resource.project = @project
- resource.key = 'CODE_QUALITY_DISABLED'
- resource.value = '1'
- resource.masked = false
- end
+ disable_optional_jobs(@project)
# Set an application secret CI variable (prefixed with K8S_SECRET_)
Resource::CiVariable.fabricate! do |resource|
diff --git a/qa/qa/specs/parallel_runner.rb b/qa/qa/specs/parallel_runner.rb
new file mode 100644
index 00000000000..b92fdb610b6
--- /dev/null
+++ b/qa/qa/specs/parallel_runner.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'open3'
+
+module QA
+ module Specs
+ module ParallelRunner
+ module_function
+
+ def run(args)
+ unless args.include?('--')
+ index = args.index { |opt| opt.include?('features') }
+
+ args.insert(index, '--') if index
+ end
+
+ env = {}
+ Runtime::Env::ENV_VARIABLES.each_key do |key|
+ env[key] = ENV[key] if ENV[key]
+ end
+ env['QA_RUNTIME_SCENARIO_ATTRIBUTES'] = Runtime::Scenario.attributes.to_json
+ env['GITLAB_QA_ACCESS_TOKEN'] = Runtime::API::Client.new(:gitlab).personal_access_token unless env['GITLAB_QA_ACCESS_TOKEN']
+
+ cmd = "bundle exec parallel_test -t rspec --combine-stderr --serialize-stdout -- #{args.flatten.join(' ')}"
+ ::Open3.popen2e(env, cmd) do |_, out, wait|
+ out.each { |line| puts line }
+
+ exit wait.value.exitstatus
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index f1cb9378de8..6aa08cf77b4 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
+require 'knapsack'
require 'rspec/core'
require 'rspec/expectations'
-require 'knapsack'
module QA
module Specs
@@ -17,44 +17,56 @@ module QA
@options = []
end
- def perform
- args = []
- args.push('--tty') if tty
+ def paths_from_knapsack
+ allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
+
+ QA::Runtime::Logger.info ''
+ QA::Runtime::Logger.info 'Report specs:'
+ QA::Runtime::Logger.info allocator.report_node_tests.join(', ')
+ QA::Runtime::Logger.info ''
+ QA::Runtime::Logger.info 'Leftover specs:'
+ QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ')
+ QA::Runtime::Logger.info ''
+
+ ['--', allocator.node_tests]
+ end
+
+ def rspec_tags
+ tags_for_rspec = []
if tags.any?
- tags.each { |tag| args.push(['--tag', tag.to_s]) }
+ tags.each { |tag| tags_for_rspec.push(['--tag', tag.to_s]) }
else
- args.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any?
+ tags_for_rspec.push(%w[--tag ~orchestrated]) unless (%w[-t --tag] & options).any?
end
- args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
+ tags_for_rspec.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
QA::Runtime::Env.supported_features.each_key do |key|
- args.push(["--tag", "~requires_#{key}"]) unless QA::Runtime::Env.can_test? key
+ tags_for_rspec.push(%W[--tag ~requires_#{key}]) unless QA::Runtime::Env.can_test? key
end
- args.push(options)
+ tags_for_rspec
+ end
- Runtime::Browser.configure!
+ def perform
+ args = []
+ args.push('--tty') if tty
+ args.push(rspec_tags)
+ args.push(options)
if Runtime::Env.knapsack?
- allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
-
- QA::Runtime::Logger.info ''
- QA::Runtime::Logger.info 'Report specs:'
- QA::Runtime::Logger.info allocator.report_node_tests.join(', ')
- QA::Runtime::Logger.info ''
- QA::Runtime::Logger.info 'Leftover specs:'
- QA::Runtime::Logger.info allocator.leftover_node_tests.join(', ')
- QA::Runtime::Logger.info ''
-
- args.push(['--', allocator.node_tests])
+ args.push(paths_from_knapsack)
else
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
end
- RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status|
- abort if status.nonzero?
+ if Runtime::Scenario.attributes[:parallel]
+ ParallelRunner.run(args.flatten)
+ else
+ RSpec::Core::Runner.run(args.flatten, $stderr, $stdout).tap do |status|
+ abort if status.nonzero?
+ end
end
end
end
diff --git a/qa/qa/tools/generate_perf_testdata.rb b/qa/qa/tools/generate_perf_testdata.rb
index b0477951967..26bcb2fe958 100644
--- a/qa/qa/tools/generate_perf_testdata.rb
+++ b/qa/qa/tools/generate_perf_testdata.rb
@@ -59,8 +59,8 @@ module QA
group_search_response = create_a_group_api_req(@group_name, @visibility)
group = JSON.parse(group_search_response.body)
@urls[:group_page] = group["web_url"]
- group["id"]
STDOUT.puts "Created a group: #{@urls[:group_page]}"
+ group["id"]
end
def create_project(group_id)
@@ -196,6 +196,7 @@ module QA
project_path = "#{@group_name}%2F#{@project_name}"
branch_name = "branch_with_many_commits-#{SecureRandom.hex(8)}"
file_name = "file_for_many_commits.txt"
+
create_a_branch_api_req(branch_name, project_path)
create_a_new_file_api_req(file_name, branch_name, project_path, "Initial commit for new file", "Initial file content")
create_mr_response = create_a_merge_request_api_req(project_path, branch_name, "master", "MR with many commits-#{SecureRandom.hex(8)}")
@@ -203,7 +204,7 @@ module QA
100.times do |i|
update_file_api_req(file_name, branch_name, project_path, Faker::Lorem.sentences(5).join(" "), Faker::Lorem.sentences(500).join("\n"))
end
- STDOUT.puts "Created an MR with many commits: #{@urls[:mr_with_many_commits]}"
+ STDOUT.puts "Using branch: #{branch_name}, created an MR with many commits: #{@urls[:mr_with_many_commits]}"
end
private
@@ -211,56 +212,88 @@ module QA
# API Requests
def create_a_discussion_on_issue_api_req(project_path_or_id, issue_id, body)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/discussions").url, "body=\"#{body}\""
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/discussions").url, "body=\"#{body}\""
+ end
end
def update_a_discussion_on_issue_api_req(project_path_or_id, mr_iid, discussion_id, resolved_status)
- put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions/#{discussion_id}").url, "resolved=#{resolved_status}"
+ call_api(expected_response_code: 200) do
+ put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions/#{discussion_id}").url, "resolved=#{resolved_status}"
+ end
end
def create_a_discussion_on_mr_api_req(project_path_or_id, mr_iid, body)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions").url,
- "body=\"#{body}\""
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests/#{mr_iid}/discussions").url, "body=\"#{body}\""
+ end
end
def create_a_label_api_req(project_path_or_id, name, color)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/labels").url, "name=#{name}&color=#{color}"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/labels").url, "name=#{name}&color=#{color}"
+ end
end
def create_a_todo_api_req(project_path_or_id, issue_id)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/todo").url, nil
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}/todo").url, nil
+ end
end
def create_an_issue_api_req(project_path_or_id, title, description)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues").url, "title=#{title}&description=#{description}"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues").url, "title=#{title}&description=#{description}"
+ end
end
def update_an_issue_api_req(project_path_or_id, issue_id, description, labels_list)
- put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}").url, "description=#{description}&labels=#{labels_list}"
+ call_api(expected_response_code: 200) do
+ put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/issues/#{issue_id}").url, "description=#{description}&labels=#{labels_list}"
+ end
end
def create_a_project_api_req(project_name, group_id, visibility)
- post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{project_name}&namespace_id=#{group_id}&visibility=#{visibility}"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{project_name}&namespace_id=#{group_id}&visibility=#{visibility}"
+ end
end
def create_a_group_api_req(group_name, visibility)
- post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{group_name}&path=#{group_name}&visibility=#{visibility}"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{group_name}&path=#{group_name}&visibility=#{visibility}"
+ end
end
def create_a_branch_api_req(branch_name, project_path_or_id)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/branches").url, "branch=#{branch_name}&ref=master"
+ end
end
def create_a_new_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\""
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\""
+ end
end
def create_a_merge_request_api_req(project_path_or_id, source_branch, target_branch, mr_title)
- post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests").url, "source_branch=#{source_branch}&target_branch=#{target_branch}&title=#{mr_title}"
+ call_api(expected_response_code: 201) do
+ post Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/merge_requests").url, "source_branch=#{source_branch}&target_branch=#{target_branch}&title=#{mr_title}"
+ end
end
def update_file_api_req(file_path, branch_name, project_path_or_id, commit_message, content)
- put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\""
+ call_api(expected_response_code: 200) do
+ put Runtime::API::Request.new(@api_client, "/projects/#{project_path_or_id}/repository/files/#{file_path}").url, "branch=#{branch_name}&commit_message=\"#{commit_message}\"&content=\"#{content}\""
+ end
+ end
+
+ def call_api(expected_response_code: 200)
+ response = yield
+ raise "API call failed with response code: #{response.code} and body: #{response.body}" unless response.code == expected_response_code
+
+ response
end
end
end
diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb
index 0f1ed039149..92a4f7b40e6 100644
--- a/qa/spec/page/logging_spec.rb
+++ b/qa/spec/page/logging_spec.rb
@@ -91,26 +91,26 @@ describe QA::Support::Page::Logging do
it 'logs has_element?' do
expect { subject.has_element?(:element) }
- .to output(/has_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process
+ .to output(/has_element\? :element \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process
end
it 'logs has_element? with text' do
expect { subject.has_element?(:element, text: "some text") }
- .to output(/has_element\? :element with text \"some text\" \(wait: 2\) returned: true/).to_stdout_from_any_process
+ .to output(/has_element\? :element with text \"some text\" \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process
end
it 'logs has_no_element?' do
allow(page).to receive(:has_no_css?).and_return(true)
expect { subject.has_no_element?(:element) }
- .to output(/has_no_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process
+ .to output(/has_no_element\? :element \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process
end
it 'logs has_no_element? with text' do
allow(page).to receive(:has_no_css?).and_return(true)
expect { subject.has_no_element?(:element, text: "more text") }
- .to output(/has_no_element\? :element with text \"more text\" \(wait: 2\) returned: true/).to_stdout_from_any_process
+ .to output(/has_no_element\? :element with text \"more text\" \(wait: #{QA::Runtime::Browser::CAPYBARA_MAX_WAIT_TIME}\) returned: true/).to_stdout_from_any_process
end
it 'logs has_text?' do
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index f25dbf3a8ab..21bfd2876a9 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -8,6 +8,10 @@ if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
Knapsack::Adapters::RSpecAdapter.bind
end
+QA::Runtime::Browser.configure!
+
+QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) if QA::Runtime::Env.runtime_scenario_attributes
+
%w[helpers shared_examples].each do |d|
Dir[::File.join(__dir__, d, '**', '*.rb')].each { |f| require f }
end
diff --git a/qa/spec/specs/parallel_runner_spec.rb b/qa/spec/specs/parallel_runner_spec.rb
new file mode 100644
index 00000000000..67d94a1f648
--- /dev/null
+++ b/qa/spec/specs/parallel_runner_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+describe QA::Specs::ParallelRunner do
+ include Helpers::StubENV
+
+ before do
+ allow(QA::Runtime::Scenario).to receive(:attributes).and_return(parallel: true)
+ stub_env('GITLAB_QA_ACCESS_TOKEN', 'skip_token_creation')
+ end
+
+ it 'passes args to parallel_tests' do
+ expect_cli_arguments(['--tag', '~orchestrated', *QA::Specs::Runner::DEFAULT_TEST_PATH_ARGS])
+
+ subject.run(['--tag', '~orchestrated', *QA::Specs::Runner::DEFAULT_TEST_PATH_ARGS])
+ end
+
+ it 'passes a given test path to parallel_tests and adds a separator' do
+ expect_cli_arguments(%w[-- qa/specs/features/foo])
+
+ subject.run(%w[qa/specs/features/foo])
+ end
+
+ it 'passes tags and test paths to parallel_tests and adds a separator' do
+ expect_cli_arguments(%w[--tag smoke -- qa/specs/features/foo qa/specs/features/bar])
+
+ subject.run(%w[--tag smoke qa/specs/features/foo qa/specs/features/bar])
+ end
+
+ it 'passes tags and test paths with separators to parallel_tests' do
+ expect_cli_arguments(%w[-- --tag smoke -- qa/specs/features/foo qa/specs/features/bar])
+
+ subject.run(%w[-- --tag smoke -- qa/specs/features/foo qa/specs/features/bar])
+ end
+
+ it 'passes supported environment variables' do
+ # Test only env vars starting with GITLAB because some of the others
+ # affect how the runner behaves, and we're not concerned with those
+ # behaviors in this test
+ gitlab_env_vars = QA::Runtime::Env::ENV_VARIABLES.reject { |v| !v.start_with?('GITLAB') }
+
+ gitlab_env_vars.each do |k, v|
+ stub_env(k, v)
+ end
+
+ gitlab_env_vars['QA_RUNTIME_SCENARIO_ATTRIBUTES'] = '{"parallel":true}'
+
+ expect_cli_arguments([], gitlab_env_vars)
+
+ subject.run([])
+ end
+
+ def expect_cli_arguments(arguments, env = { 'QA_RUNTIME_SCENARIO_ATTRIBUTES' => '{"parallel":true}' })
+ cmd = "bundle exec parallel_test -t rspec --combine-stderr --serialize-stdout -- #{arguments.join(' ')}"
+ expect(Open3).to receive(:popen2e)
+ .with(hash_including(env), cmd)
+ .and_return(0)
+ end
+end
diff --git a/qa/spec/specs/runner_spec.rb b/qa/spec/specs/runner_spec.rb
index f94145d148e..6c533c6dc7d 100644
--- a/qa/spec/specs/runner_spec.rb
+++ b/qa/spec/specs/runner_spec.rb
@@ -58,11 +58,11 @@ describe QA::Specs::Runner do
end
end
- context 'when "-- qa/specs/features/foo" is set as options' do
- subject { described_class.new.tap { |runner| runner.options = %w[-- qa/specs/features/foo] } }
+ context 'when "--tag smoke" and "qa/specs/features/foo" are set as options' do
+ subject { described_class.new.tap { |runner| runner.options = %w[--tag smoke qa/specs/features/foo] } }
- it 'passes the given tests path and excludes the orchestrated tag' do
- expect_rspec_runner_arguments(['--tag', '~orchestrated', '--', 'qa/specs/features/foo'])
+ it 'focuses on the given tag and includes the path without excluding the orchestrated tag' do
+ expect_rspec_runner_arguments(['--tag', 'smoke', 'qa/specs/features/foo'])
subject.perform
end
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 633ea28e96c..2bf654b1e24 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -131,6 +131,7 @@ function install_external_dns() {
if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${release_name}" ; then
echoinfo "Installing external-dns Helm chart"
helm repo update
+ # Default requested: CPU => 0, memory => 0
helm install stable/external-dns \
-n "${release_name}" \
--namespace "${KUBE_NAMESPACE}" \
@@ -141,7 +142,11 @@ function install_external_dns() {
--set domainFilters[0]="${domain}" \
--set txtOwnerId="${KUBE_NAMESPACE}" \
--set rbac.create="true" \
- --set policy="sync"
+ --set policy="sync" \
+ --set resources.requests.cpu=50m \
+ --set resources.limits.cpu=100m \
+ --set resources.requests.memory=100M \
+ --set resources.limits.memory=200M
else
echoinfo "The external-dns Helm chart is already successfully deployed."
fi
@@ -196,45 +201,122 @@ HELM_CMD=$(cat << EOF
helm upgrade --install \
--wait \
--timeout 600 \
- --set global.appConfig.enableUsagePing=false \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
+ --set global.appConfig.enableUsagePing=false \
--set global.imagePullPolicy=Always \
--set global.hosts.hostSuffix="$HOST_SUFFIX" \
--set global.hosts.domain="$REVIEW_APPS_DOMAIN" \
- --set certmanager.install=false \
- --set prometheus.install=false \
--set global.ingress.configureCertmanager=false \
--set global.ingress.tls.secretName=tls-cert \
--set global.ingress.annotations."external-dns\.alpha\.kubernetes\.io/ttl"="10" \
+ --set certmanager.install=false \
+ --set prometheus.install=false \
--set nginx-ingress.controller.service.enableHttp=false \
- --set nginx-ingress.defaultBackend.resources.requests.memory=7Mi \
- --set nginx-ingress.controller.resources.requests.memory=440M \
--set nginx-ingress.controller.replicaCount=2 \
- --set gitlab.unicorn.resources.requests.cpu=200m \
- --set gitlab.sidekiq.resources.requests.cpu=100m \
- --set gitlab.sidekiq.resources.requests.memory=800M \
- --set gitlab.gitlab-shell.resources.requests.cpu=100m \
- --set redis.resources.requests.cpu=100m \
- --set minio.resources.requests.cpu=100m \
+ --set nginx-ingress.controller.config.ssl-ciphers="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4" \
--set gitlab.migrations.image.repository="$gitlab_migrations_image_repository" \
--set gitlab.migrations.image.tag="$CI_COMMIT_REF_SLUG" \
- --set gitlab.sidekiq.image.repository="$gitlab_sidekiq_image_repository" \
- --set gitlab.sidekiq.image.tag="$CI_COMMIT_REF_SLUG" \
- --set gitlab.unicorn.image.repository="$gitlab_unicorn_image_repository" \
- --set gitlab.unicorn.image.tag="$CI_COMMIT_REF_SLUG" \
- --set gitlab.task-runner.image.repository="$gitlab_task_runner_image_repository" \
- --set gitlab.task-runner.image.tag="$CI_COMMIT_REF_SLUG" \
--set gitlab.gitaly.image.repository="$gitlab_gitaly_image_repository" \
--set gitlab.gitaly.image.tag="v$GITALY_VERSION" \
--set gitlab.gitlab-shell.image.repository="$gitlab_shell_image_repository" \
--set gitlab.gitlab-shell.image.tag="v$GITLAB_SHELL_VERSION" \
+ --set gitlab.sidekiq.image.repository="$gitlab_sidekiq_image_repository" \
+ --set gitlab.sidekiq.image.tag="$CI_COMMIT_REF_SLUG" \
+ --set gitlab.unicorn.image.repository="$gitlab_unicorn_image_repository" \
+ --set gitlab.unicorn.image.tag="$CI_COMMIT_REF_SLUG" \
--set gitlab.unicorn.workhorse.image="$gitlab_workhorse_image_repository" \
--set gitlab.unicorn.workhorse.tag="$CI_COMMIT_REF_SLUG" \
- --set nginx-ingress.controller.config.ssl-ciphers="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4" \
- --namespace="$KUBE_NAMESPACE" \
- --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
- "$name" \
- .
+ --set gitlab.task-runner.image.repository="$gitlab_task_runner_image_repository" \
+ --set gitlab.task-runner.image.tag="$CI_COMMIT_REF_SLUG"
+EOF
+)
+
+# Default requested: CPU => 100m, memory => 100Mi
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set nginx-ingress.controller.resources.limits.cpu=200m \
+ --set nginx-ingress.controller.resources.requests.memory=210M \
+ --set nginx-ingress.controller.resources.limits.memory=420M
+EOF
+)
+
+# Default requested: CPU => 5m, memory => 5Mi
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set nginx-ingress.defaultBackend.resources.limits.cpu=10m \
+ --set nginx-ingress.defaultBackend.resources.requests.memory=12M \
+ --set nginx-ingress.defaultBackend.resources.limits.memory=24M
+EOF
+)
+
+# Default requested: CPU => 100m, memory => 200Mi
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set gitlab.gitaly.resources.requests.cpu=150m \
+ --set gitlab.gitaly.resources.limits.cpu=300m \
+ --set gitlab.gitaly.resources.limits.memory=420M
+EOF
+)
+
+# Default requested: CPU => 0, memory => 6M
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set gitlab.gitlab-shell.resources.requests.cpu=70m \
+ --set gitlab.gitlab-shell.resources.limits.cpu=140m \
+ --set gitlab.gitlab-shell.resources.requests.memory=20M \
+ --set gitlab.gitlab-shell.resources.limits.memory=40M
+EOF
+)
+
+# Default requested: CPU => 50m, memory => 650M
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set gitlab.sidekiq.resources.requests.cpu=200m \
+ --set gitlab.sidekiq.resources.limits.cpu=300m \
+ --set gitlab.sidekiq.resources.requests.memory=800M \
+ --set gitlab.sidekiq.resources.limits.memory=1.2G
+EOF
+)
+
+# Default requested: CPU => 300m + 100m (workhorse), memory => 1.2G + 100M (workhorse)
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set gitlab.unicorn.resources.limits.cpu=800m \
+ --set gitlab.unicorn.resources.limits.memory=2.6G
+EOF
+)
+
+# Default requested: CPU => 100m, memory => 64Mi
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set redis.resources.limits.cpu=200m \
+ --set redis.resources.limits.memory=130M
+EOF
+)
+
+# Default requested: CPU => 100m, memory => 128Mi
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set minio.resources.limits.cpu=200m \
+ --set minio.resources.limits.memory=280M
+EOF
+)
+
+# Default requested: CPU => 0, memory => 0
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --set gitlab-runner.resources.requests.cpu=300m \
+ --set gitlab-runner.resources.limits.cpu=600m \
+ --set gitlab-runner.resources.requests.memory=300M \
+ --set gitlab-runner.resources.limits.memory=600M
+EOF
+)
+
+HELM_CMD=$(cat << EOF
+ $HELM_CMD \
+ --namespace="$KUBE_NAMESPACE" \
+ --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
+ "$name" .
EOF
)
diff --git a/scripts/trigger-build-docs b/scripts/trigger-build-docs
index dfc8ee6050a..0fc8f6fbd4b 100755
--- a/scripts/trigger-build-docs
+++ b/scripts/trigger-build-docs
@@ -105,8 +105,8 @@ def trigger_pipeline
puts ""
puts app_url
puts ""
- puts "=> For more information, read the documentation"
- puts "=> https://docs.gitlab.com/ee/development/writing_documentation.html#previewing-the-changes-live"
+ puts "=> For more information, see the documentation"
+ puts "=> https://docs.gitlab.com/ee/development/documentation/index.html#previewing-the-changes-live"
puts ""
puts "=> If something doesn't work, drop a line in the #docs chat channel."
puts ""
diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb
index ea68eae12ed..6591901a9dc 100644
--- a/spec/controllers/dashboard/projects_controller_spec.rb
+++ b/spec/controllers/dashboard/projects_controller_spec.rb
@@ -11,8 +11,10 @@ describe Dashboard::ProjectsController do
end
context 'user logged in' do
+ let(:user) { create(:user) }
+
before do
- sign_in create(:user)
+ sign_in(user)
end
context 'external authorization' do
@@ -24,6 +26,20 @@ describe Dashboard::ProjectsController do
expect(response).to have_gitlab_http_status(200)
end
end
+
+ it 'orders the projects by last activity by default' do
+ project = create(:project)
+ project.add_developer(user)
+ project.update!(last_repository_updated_at: 3.days.ago, last_activity_at: 3.days.ago)
+
+ project2 = create(:project)
+ project2.add_developer(user)
+ project2.update!(last_repository_updated_at: 10.days.ago, last_activity_at: 10.days.ago)
+
+ get :index
+
+ expect(assigns(:projects)).to eq([project, project2])
+ end
end
end
diff --git a/spec/controllers/projects/deployments_controller_spec.rb b/spec/controllers/projects/deployments_controller_spec.rb
index 95417936df4..b9ee69a617b 100644
--- a/spec/controllers/projects/deployments_controller_spec.rb
+++ b/spec/controllers/projects/deployments_controller_spec.rb
@@ -41,34 +41,26 @@ describe Projects::DeploymentsController do
describe 'GET #metrics' do
let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
- before do
- allow(controller).to receive(:deployment).and_return(deployment)
- end
-
context 'when metrics are disabled' do
- before do
- allow(deployment).to receive(:has_metrics?).and_return false
- end
-
it 'responds with not found' do
- get :metrics, params: deployment_params(id: deployment.id)
+ get :metrics, params: deployment_params(id: deployment.to_param)
expect(response).to be_not_found
end
end
context 'when metrics are enabled' do
- before do
- allow(deployment).to receive(:has_metrics?).and_return true
- end
-
context 'when environment has no metrics' do
before do
- expect(deployment).to receive(:metrics).and_return(nil)
+ expect_next_instance_of(DeploymentMetrics) do |deployment_metrics|
+ allow(deployment_metrics).to receive(:has_metrics?).and_return(true)
+
+ expect(deployment_metrics).to receive(:metrics).and_return(nil)
+ end
end
it 'returns a empty response 204 resposne' do
- get :metrics, params: deployment_params(id: deployment.id)
+ get :metrics, params: deployment_params(id: deployment.to_param)
expect(response).to have_gitlab_http_status(204)
expect(response.body).to eq('')
end
@@ -84,11 +76,15 @@ describe Projects::DeploymentsController do
end
before do
- expect(deployment).to receive(:metrics).and_return(empty_metrics)
+ expect_next_instance_of(DeploymentMetrics) do |deployment_metrics|
+ allow(deployment_metrics).to receive(:has_metrics?).and_return(true)
+
+ expect(deployment_metrics).to receive(:metrics).and_return(empty_metrics)
+ end
end
it 'returns a metrics JSON document' do
- get :metrics, params: deployment_params(id: deployment.id)
+ get :metrics, params: deployment_params(id: deployment.to_param)
expect(response).to be_ok
expect(json_response['success']).to be(true)
@@ -96,54 +92,32 @@ describe Projects::DeploymentsController do
expect(json_response['last_update']).to eq(42)
end
end
-
- context 'when metrics service does not implement deployment metrics' do
- before do
- allow(deployment).to receive(:metrics).and_raise(NotImplementedError)
- end
-
- it 'responds with not found' do
- get :metrics, params: deployment_params(id: deployment.id)
-
- expect(response).to be_not_found
- end
- end
end
end
describe 'GET #additional_metrics' do
let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
- before do
- allow(controller).to receive(:deployment).and_return(deployment)
- end
-
context 'when metrics are disabled' do
- before do
- allow(deployment).to receive(:has_metrics?).and_return false
- end
-
it 'responds with not found' do
- get :metrics, params: deployment_params(id: deployment.id)
+ get :metrics, params: deployment_params(id: deployment.to_param)
expect(response).to be_not_found
end
end
context 'when metrics are enabled' do
- let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
-
- before do
- allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter)
- end
-
context 'when environment has no metrics' do
before do
- expect(deployment).to receive(:additional_metrics).and_return({})
+ expect_next_instance_of(DeploymentMetrics) do |deployment_metrics|
+ allow(deployment_metrics).to receive(:has_metrics?).and_return(true)
+
+ expect(deployment_metrics).to receive(:additional_metrics).and_return({})
+ end
end
it 'returns a empty response 204 response' do
- get :additional_metrics, params: deployment_params(id: deployment.id, format: :json)
+ get :additional_metrics, params: deployment_params(id: deployment.to_param, format: :json)
expect(response).to have_gitlab_http_status(204)
expect(response.body).to eq('')
end
@@ -159,11 +133,15 @@ describe Projects::DeploymentsController do
end
before do
- expect(deployment).to receive(:additional_metrics).and_return(empty_metrics)
+ expect_next_instance_of(DeploymentMetrics) do |deployment_metrics|
+ allow(deployment_metrics).to receive(:has_metrics?).and_return(true)
+
+ expect(deployment_metrics).to receive(:additional_metrics).and_return(empty_metrics)
+ end
end
it 'returns a metrics JSON document' do
- get :additional_metrics, params: deployment_params(id: deployment.id, format: :json)
+ get :additional_metrics, params: deployment_params(id: deployment.to_param, format: :json)
expect(response).to be_ok
expect(json_response['success']).to be(true)
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 0eca663a683..9878f88a395 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -878,6 +878,22 @@ describe Projects::MergeRequestsController do
expect(control_count).to be <= 137
end
+ it 'has no N+1 issues for environments', :request_store, retry: 0 do
+ # First run to insert test data from lets, which does take up some 30 queries
+ get_ci_environments_status
+
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { get_ci_environments_status }.count
+
+ environment2 = create(:environment, project: forked)
+ create(:deployment, :succeed, environment: environment2, sha: sha, ref: 'master', deployable: build)
+
+ # TODO address the last 11 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
+ expect { get_ci_environments_status }.not_to exceed_all_query_limit(control_count + leeway)
+ end
+
def get_ci_environments_status(extra_params = {})
params = {
namespace_id: merge_request.project.namespace.to_param,
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 8d2412f97ef..4e1cac67d23 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -318,6 +318,53 @@ describe ProjectsController do
end
end
+ describe '#housekeeping' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let(:housekeeping) { Projects::HousekeepingService.new(project) }
+
+ context 'when authenticated as owner' do
+ before do
+ group.add_owner(user)
+ sign_in(user)
+
+ allow(Projects::HousekeepingService).to receive(:new).with(project, :gc).and_return(housekeeping)
+ end
+
+ it 'forces a full garbage collection' do
+ expect(housekeeping).to receive(:execute).once
+
+ post :housekeeping,
+ params: {
+ namespace_id: project.namespace.path,
+ id: project.path
+ }
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+
+ context 'when authenticated as developer' do
+ let(:developer) { create(:user) }
+
+ before do
+ group.add_developer(developer)
+ end
+
+ it 'does not execute housekeeping' do
+ expect(housekeeping).not_to receive(:execute)
+
+ post :housekeeping,
+ params: {
+ namespace_id: project.namespace.path,
+ id: project.path
+ }
+
+ expect(response).to have_gitlab_http_status(302)
+ end
+ end
+ end
+
describe "#update" do
render_views
diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb
index b5323a1c76d..ecd0aab925b 100644
--- a/spec/features/admin/admin_sees_project_statistics_spec.rb
+++ b/spec/features/admin/admin_sees_project_statistics_spec.rb
@@ -15,7 +15,7 @@ describe "Admin > Admin sees project statistics" do
let(:project) { create(:project, :repository) }
it "shows project statistics" do
- expect(page).to have_content("Storage: 0 Bytes (0 Bytes repositories, 0 Bytes wikis, 0 Bytes build artifacts, 0 Bytes LFS)")
+ expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes)")
end
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 55f5ff04d01..254bb12573c 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -22,7 +22,7 @@ describe 'Dashboard shortcuts', :js do
find('body').send_keys([:shift, 'T'])
- check_page_title('Todos')
+ check_page_title('To-Do List')
find('body').send_keys([:shift, 'P'])
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index d58e3b2841e..c48229fc0a0 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -13,7 +13,7 @@ describe 'Dashboard Todos' do
end
it 'shows "All done" message' do
- expect(page).to have_content 'Todos let you see what you should do next'
+ expect(page).to have_content 'Your To-Do List shows what to work on next'
end
end
@@ -72,7 +72,7 @@ describe 'Dashboard Todos' do
end
it 'updates todo count' do
- expect(page).to have_content 'Todos 0'
+ expect(page).to have_content 'To Do 0'
expect(page).to have_content 'Done 1'
end
@@ -101,7 +101,7 @@ describe 'Dashboard Todos' do
end
it 'updates todo count' do
- expect(page).to have_content 'Todos 1'
+ expect(page).to have_content 'To Do 1'
expect(page).to have_content 'Done 0'
end
end
@@ -211,7 +211,7 @@ describe 'Dashboard Todos' do
describe 'restoring the todo' do
before do
within first('.todo') do
- click_link 'Add todo'
+ click_link 'Add a To Do'
end
end
@@ -220,7 +220,7 @@ describe 'Dashboard Todos' do
end
it 'updates todo count' do
- expect(page).to have_content 'Todos 1'
+ expect(page).to have_content 'To Do 1'
expect(page).to have_content 'Done 0'
end
end
@@ -276,7 +276,7 @@ describe 'Dashboard Todos' do
end
it 'shows "All done" message!' do
- expect(page).to have_content 'Todos 0'
+ expect(page).to have_content 'To Do 0'
expect(page).to have_content "You're all done!"
expect(page).not_to have_selector('.gl-pagination')
end
@@ -303,7 +303,7 @@ describe 'Dashboard Todos' do
it 'updates todo count' do
mark_all_and_undo
- expect(page).to have_content 'Todos 2'
+ expect(page).to have_content 'To Do 2'
expect(page).to have_content 'Done 0'
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 0114178b9be..07ae159eef4 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -13,8 +13,8 @@ describe 'Manually create a todo item from issue', :js do
it 'creates todo when clicking button' do
page.within '.issuable-sidebar' do
- click_button 'Add todo'
- expect(page).to have_content 'Mark todo as done'
+ click_button 'Add a To Do'
+ expect(page).to have_content 'Mark as done'
end
page.within '.header-content .todos-count' do
@@ -30,8 +30,8 @@ describe 'Manually create a todo item from issue', :js do
it 'marks a todo as done' do
page.within '.issuable-sidebar' do
- click_button 'Add todo'
- click_button 'Mark todo as done'
+ click_button 'Add a To Do'
+ click_button 'Mark as done'
end
expect(page).to have_selector('.todos-count', visible: false)
diff --git a/spec/features/issues/user_creates_confidential_merge_request_spec.rb b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
new file mode 100644
index 00000000000..7ae4af4667b
--- /dev/null
+++ b/spec/features/issues/user_creates_confidential_merge_request_spec.rb
@@ -0,0 +1,54 @@
+require 'rails_helper'
+
+describe 'User creates confidential merge request on issue page', :js do
+ include ProjectForksHelper
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository, :public) }
+ let(:issue) { create(:issue, project: project, confidential: true) }
+
+ def visit_confidential_issue
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_requests
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ context 'user has no private fork' do
+ before do
+ fork_project(project, user, repository: true)
+ visit_confidential_issue
+ end
+
+ it 'shows that user has no fork available' do
+ click_button 'Create confidential merge request'
+
+ page.within '.create-confidential-merge-request-dropdown-menu' do
+ expect(page).to have_content('No forks available to you')
+ end
+ end
+ end
+
+ describe 'user has private fork' do
+ let(:forked_project) { fork_project(project, user, repository: true) }
+
+ before do
+ forked_project.update(visibility: Gitlab::VisibilityLevel::PRIVATE)
+ visit_confidential_issue
+ end
+
+ it 'create merge request in fork' do
+ click_button 'Create confidential merge request'
+
+ page.within '.create-confidential-merge-request-dropdown-menu' do
+ expect(page).to have_button(forked_project.name_with_namespace)
+ click_button 'Create confidential merge request'
+ end
+
+ expect(page).to have_content(forked_project.namespace.name)
+ end
+ end
+end
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 733e8aa3eba..30e30751693 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -519,6 +519,8 @@ describe 'Merge request > User sees merge widget', :js do
end
before do
+ allow_any_instance_of(TestSuiteComparerEntity)
+ .to receive(:max_tests).and_return(2)
allow_any_instance_of(MergeRequest)
.to receive(:has_test_reports?).and_return(true)
allow_any_instance_of(MergeRequest)
@@ -551,7 +553,7 @@ describe 'Merge request > User sees merge widget', :js do
expect(page).to have_content('rspec found no changed test results out of 1 total test')
expect(page).to have_content('junit found 1 failed test result out of 1 total test')
expect(page).to have_content('New')
- expect(page).to have_content('subtractTest')
+ expect(page).to have_content('addTest')
end
end
end
@@ -562,7 +564,7 @@ describe 'Merge request > User sees merge widget', :js do
click_button 'Expand'
within(".js-report-section-container") do
- click_button 'subtractTest'
+ click_button 'addTest'
expect(page).to have_content('6.66')
expect(page).to have_content(sample_java_failed_message.gsub!(/\s+/, ' ').strip)
@@ -596,7 +598,7 @@ describe 'Merge request > User sees merge widget', :js do
expect(page).to have_content('rspec found 1 failed test result out of 1 total test')
expect(page).to have_content('junit found no changed test results out of 1 total test')
expect(page).not_to have_content('New')
- expect(page).to have_content('Test#sum when a is 2 and b is 2 returns summary')
+ expect(page).to have_content('Test#sum when a is 1 and b is 3 returns summary')
end
end
end
@@ -607,7 +609,7 @@ describe 'Merge request > User sees merge widget', :js do
click_button 'Expand'
within(".js-report-section-container") do
- click_button 'Test#sum when a is 2 and b is 2 returns summary'
+ click_button 'Test#sum when a is 1 and b is 3 returns summary'
expect(page).to have_content('2.22')
expect(page).to have_content(sample_rspec_failed_message.gsub!(/\s+/, ' ').strip)
@@ -628,13 +630,7 @@ describe 'Merge request > User sees merge widget', :js do
let(:head_reports) do
Gitlab::Ci::Reports::TestReports.new.tap do |reports|
reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
- reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
- end
- end
-
- let(:create_test_case_java_resolved) do
- create_test_case_java_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
+ reports.get_suite('junit').add_test_case(create_test_case_java_success)
end
end
@@ -646,7 +642,7 @@ describe 'Merge request > User sees merge widget', :js do
within(".js-report-section-container") do
expect(page).to have_content('rspec found no changed test results out of 1 total test')
expect(page).to have_content('junit found 1 fixed test result out of 1 total test')
- expect(page).to have_content('subtractTest')
+ expect(page).to have_content('addTest')
end
end
end
@@ -657,15 +653,53 @@ describe 'Merge request > User sees merge widget', :js do
click_button 'Expand'
within(".js-report-section-container") do
- click_button 'subtractTest'
+ click_button 'addTest'
- expect(page).to have_content('6.66')
+ expect(page).to have_content('5.55')
end
end
end
end
end
+ context 'properly truncates the report' do
+ let(:base_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ 10.times do |index|
+ reports.get_suite('rspec').add_test_case(
+ create_test_case_rspec_failed(index))
+ reports.get_suite('junit').add_test_case(
+ create_test_case_java_success(index))
+ end
+ end
+ end
+
+ let(:head_reports) do
+ Gitlab::Ci::Reports::TestReports.new.tap do |reports|
+ 10.times do |index|
+ reports.get_suite('rspec').add_test_case(
+ create_test_case_rspec_failed(index))
+ reports.get_suite('junit').add_test_case(
+ create_test_case_java_failed(index))
+ end
+ end
+ end
+
+ it 'shows test reports summary which includes the resolved failure' do
+ within(".js-reports-container") do
+ click_button 'Expand'
+
+ expect(page).to have_content('Test summary contained 20 failed test results out of 20 total tests')
+ within(".js-report-section-container") do
+ expect(page).to have_content('rspec found 10 failed test results out of 10 total tests')
+ expect(page).to have_content('junit found 10 failed test results out of 10 total tests')
+
+ expect(page).to have_content('Test#sum when a is 1 and b is 3 returns summary', count: 2)
+ end
+ end
+ end
+ end
+
def comparer
Gitlab::Ci::Reports::TestReportsComparer.new(base_reports, head_reports)
end
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index 5ebfc32952d..86331728f88 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -16,16 +16,8 @@ describe 'OAuth Login', :js, :allow_forgery_protection do
providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2,
:facebook, :cas3, :auth0, :authentiq, :salesforce]
- before(:all) do
- # The OmniAuth `full_host` parameter doesn't get set correctly (it gets set to something like `http://localhost`
- # here), and causes integration tests to fail with 404s. We set the `full_host` by removing the request path (and
- # anything after it) from the request URI.
- @omniauth_config_full_host = OmniAuth.config.full_host
- OmniAuth.config.full_host = ->(request) { request['REQUEST_URI'].sub(/#{request['REQUEST_PATH']}.*/, '') }
- end
-
- after(:all) do
- OmniAuth.config.full_host = @omniauth_config_full_host
+ around(:all) do |example|
+ with_omniauth_full_host { example.run }
end
def login_with_provider(provider, enter_two_factor: false)
diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb
index 1cc47cd6bd1..7ddd5c12cdf 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'User uses header search field' do
+describe 'User uses header search field', :js do
include FilteredSearchHelpers
let(:project) { create(:project) }
@@ -11,57 +11,12 @@ describe 'User uses header search field' do
sign_in(user)
end
- context 'when user is in a global scope', :js do
+ shared_examples 'search field examples' do
before do
- visit(root_path)
- page.find('#search').click
+ visit(url)
end
- context 'when clicking issues' do
- it 'shows assigned issues' do
- find('.search-input-container .dropdown-menu').click_link('Issues assigned to me')
-
- expect(page).to have_selector('.filtered-search')
- expect_tokens([assignee_token(user.name)])
- expect_filtered_search_input_empty
- end
-
- it 'shows created issues' do
- find('.search-input-container .dropdown-menu').click_link("Issues I've created")
-
- expect(page).to have_selector('.filtered-search')
- expect_tokens([author_token(user.name)])
- expect_filtered_search_input_empty
- end
- end
-
- context 'when clicking merge requests' do
- let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) }
-
- it 'shows assigned merge requests' do
- find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me')
-
- expect(page).to have_selector('.filtered-search')
- expect_tokens([assignee_token(user.name)])
- expect_filtered_search_input_empty
- end
-
- it 'shows created merge requests' do
- find('.search-input-container .dropdown-menu').click_link("Merge requests I've created")
-
- expect(page).to have_selector('.filtered-search')
- expect_tokens([author_token(user.name)])
- expect_filtered_search_input_empty
- end
- end
- end
-
- context 'when user is in a project scope' do
- before do
- visit(project_path(project))
- end
-
- it 'starts searching by pressing the enter key', :js do
+ it 'starts searching by pressing the enter key' do
fill_in('search', with: 'gitlab')
find('#search').native.send_keys(:enter)
@@ -70,30 +25,31 @@ describe 'User uses header search field' do
end
end
- context 'when clicking the search field', :js do
+ context 'when clicking the search field' do
before do
page.find('#search').click
+ wait_for_all_requests
end
it 'shows category search dropdown' do
- expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i)
+ expect(page).to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
context 'when clicking issues' do
let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
it 'shows assigned issues' do
- find('.dropdown-menu').click_link('Issues assigned to me')
+ find('.search-input-container .dropdown-menu').click_link('Issues assigned to me')
- expect(page).to have_selector('.filtered-search')
+ expect(page).to have_selector('.issues-list .issue')
expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
it 'shows created issues' do
- find('.dropdown-menu').click_link("Issues I've created")
+ find('.search-input-container .dropdown-menu').click_link("Issues I've created")
- expect(page).to have_selector('.filtered-search')
+ expect(page).to have_selector('.issues-list .issue')
expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
@@ -103,33 +59,77 @@ describe 'User uses header search field' do
let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignees: [user]) }
it 'shows assigned merge requests' do
- find('.dropdown-menu').click_link('Merge requests assigned to me')
+ find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me')
- expect(page).to have_selector('.merge-requests-holder')
+ expect(page).to have_selector('.mr-list .merge-request')
expect_tokens([assignee_token(user.name)])
expect_filtered_search_input_empty
end
it 'shows created merge requests' do
- find('.dropdown-menu').click_link("Merge requests I've created")
+ find('.search-input-container .dropdown-menu').click_link("Merge requests I've created")
- expect(page).to have_selector('.merge-requests-holder')
+ expect(page).to have_selector('.mr-list .merge-request')
expect_tokens([author_token(user.name)])
expect_filtered_search_input_empty
end
end
end
- context 'when entering text into the search field', :js do
+ context 'when entering text into the search field' do
before do
page.within('.search-input-wrap') do
- fill_in('search', with: project.name[0..3])
+ fill_in('search', with: scope_name.first(4))
end
end
it 'does not display the category search dropdown' do
- expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i)
+ expect(page).not_to have_selector('.dropdown-header', text: /#{scope_name}/i)
end
end
end
+
+ context 'when user is in a global scope' do
+ include_examples 'search field examples' do
+ let(:url) { root_path }
+ let(:scope_name) { 'All GitLab' }
+ end
+ end
+
+ context 'when user is in a project scope' do
+ include_examples 'search field examples' do
+ let(:url) { project_path(project) }
+ let(:scope_name) { project.name }
+ end
+ end
+
+ context 'when user is in a group scope' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+
+ before do
+ group.add_maintainer(user)
+ end
+
+ include_examples 'search field examples' do
+ let(:url) { group_path(group) }
+ let(:scope_name) { group.name }
+ end
+ end
+
+ context 'when user is in a subgroup scope' do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, :public, parent: group) }
+ let(:project) { create(:project, namespace: subgroup) }
+
+ before do
+ group.add_owner(user)
+ subgroup.add_owner(user)
+ end
+
+ include_examples 'search field examples' do
+ let(:url) { group_path(subgroup) }
+ let(:scope_name) { subgroup.name }
+ end
+ end
end
diff --git a/spec/finders/runner_jobs_finder_spec.rb b/spec/finders/runner_jobs_finder_spec.rb
index 97304170c4e..01f45a37ba8 100644
--- a/spec/finders/runner_jobs_finder_spec.rb
+++ b/spec/finders/runner_jobs_finder_spec.rb
@@ -35,5 +35,27 @@ describe RunnerJobsFinder do
end
end
end
+
+ context 'when order_by and sort are specified' do
+ context 'when order_by id and sort is asc' do
+ let(:params) { { order_by: 'id', sort: 'asc' } }
+ let!(:jobs) { create_list(:ci_build, 2, runner: runner, project: project, user: create(:user)) }
+
+ it 'sorts as id: :asc' do
+ is_expected.to eq(jobs.sort_by(&:id))
+ end
+ end
+ end
+
+ context 'when order_by is specified and sort is not specified' do
+ context 'when order_by id and sort is not specified' do
+ let(:params) { { order_by: 'id' } }
+ let!(:jobs) { create_list(:ci_build, 2, runner: runner, project: project, user: create(:user)) }
+
+ it 'sorts as id: :desc' do
+ is_expected.to eq(jobs.sort_by(&:id).reverse)
+ end
+ end
+ end
end
end
diff --git a/spec/frontend/api_spec.js b/spec/frontend/api_spec.js
index 0188d12a57d..7004373be0e 100644
--- a/spec/frontend/api_spec.js
+++ b/spec/frontend/api_spec.js
@@ -412,6 +412,22 @@ describe('Api', () => {
});
});
+ describe('user counts', () => {
+ it('fetches single user counts', done => {
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/user_counts`;
+ mock.onGet(expectedUrl).reply(200, {
+ merge_requests: 4,
+ });
+
+ Api.userCounts()
+ .then(({ data }) => {
+ expect(data.merge_requests).toBe(4);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
describe('user status', () => {
it('fetches single user status', done => {
const userId = '123456';
diff --git a/spec/frontend/boards/services/board_service_spec.js b/spec/frontend/boards/services/board_service_spec.js
index de9fc998360..a8a322e7237 100644
--- a/spec/frontend/boards/services/board_service_spec.js
+++ b/spec/frontend/boards/services/board_service_spec.js
@@ -2,6 +2,7 @@ import BoardService from '~/boards/services/board_service';
import { TEST_HOST } from 'helpers/test_constants';
import AxiosMockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
+import boardsStore from '~/boards/stores/boards_store';
describe('BoardService', () => {
const dummyResponse = "without type checking this doesn't matter";
@@ -18,10 +19,11 @@ describe('BoardService', () => {
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
- service = new BoardService({
+ boardsStore.setEndpoints({
...endpoints,
boardId,
});
+ service = new BoardService();
});
describe('all', () => {
diff --git a/spec/frontend/branches/divergence_graph_spec.js b/spec/frontend/branches/divergence_graph_spec.js
index 4ed77c3a036..8283bc966e4 100644
--- a/spec/frontend/branches/divergence_graph_spec.js
+++ b/spec/frontend/branches/divergence_graph_spec.js
@@ -10,12 +10,14 @@ describe('Divergence graph', () => {
mock.onGet('/-/diverging_counts').reply(200, {
master: { ahead: 1, behind: 1 },
+ 'test/hello-world': { ahead: 1, behind: 1 },
});
jest.spyOn(axios, 'get');
document.body.innerHTML = `
- <div class="js-branch-item" data-name="master"></div>
+ <div class="js-branch-item" data-name="master"><div class="js-branch-divergence-graph"></div></div>
+ <div class="js-branch-item" data-name="test/hello-world"><div class="js-branch-divergence-graph"></div></div>
`;
});
@@ -26,7 +28,13 @@ describe('Divergence graph', () => {
it('calls axos get with list of branch names', () =>
init('/-/diverging_counts').then(() => {
expect(axios.get).toHaveBeenCalledWith('/-/diverging_counts', {
- params: { names: ['master'] },
+ params: { names: ['master', 'test/hello-world'] },
});
}));
+
+ it('creates Vue components', () =>
+ init('/-/diverging_counts').then(() => {
+ expect(document.querySelector('[data-name="master"]').innerHTML).not.toEqual('');
+ expect(document.querySelector('[data-name="test/hello-world"]').innerHTML).not.toEqual('');
+ }));
});
diff --git a/spec/frontend/commons/nav/user_merge_requests_spec.js b/spec/frontend/commons/nav/user_merge_requests_spec.js
new file mode 100644
index 00000000000..4da6d53557a
--- /dev/null
+++ b/spec/frontend/commons/nav/user_merge_requests_spec.js
@@ -0,0 +1,113 @@
+import {
+ openUserCountsBroadcast,
+ closeUserCountsBroadcast,
+ refreshUserMergeRequestCounts,
+} from '~/commons/nav/user_merge_requests';
+import Api from '~/api';
+
+jest.mock('~/api');
+
+const TEST_COUNT = 1000;
+const MR_COUNT_CLASS = 'merge-requests-count';
+
+describe('User Merge Requests', () => {
+ let channelMock;
+ let newBroadcastChannelMock;
+
+ beforeEach(() => {
+ global.gon.current_user_id = 123;
+
+ channelMock = {
+ postMessage: jest.fn(),
+ close: jest.fn(),
+ };
+ newBroadcastChannelMock = jest.fn().mockImplementation(() => channelMock);
+
+ global.BroadcastChannel = newBroadcastChannelMock;
+ setFixtures(`<div class="${MR_COUNT_CLASS}">0</div>`);
+ });
+
+ const findMRCountText = () => document.body.querySelector(`.${MR_COUNT_CLASS}`).textContent;
+
+ describe('refreshUserMergeRequestCounts', () => {
+ beforeEach(() => {
+ Api.userCounts.mockReturnValue(
+ Promise.resolve({
+ data: { merge_requests: TEST_COUNT },
+ }),
+ );
+ });
+
+ describe('with open broadcast channel', () => {
+ beforeEach(() => {
+ openUserCountsBroadcast();
+
+ return refreshUserMergeRequestCounts();
+ });
+
+ it('updates the top count of merge requests', () => {
+ expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString());
+ });
+
+ it('calls the API', () => {
+ expect(Api.userCounts).toHaveBeenCalled();
+ });
+
+ it('posts count to BroadcastChannel', () => {
+ expect(channelMock.postMessage).toHaveBeenCalledWith(TEST_COUNT);
+ });
+ });
+
+ describe('without open broadcast channel', () => {
+ beforeEach(() => refreshUserMergeRequestCounts());
+
+ it('does not post anything', () => {
+ expect(channelMock.postMessage).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('openUserCountsBroadcast', () => {
+ beforeEach(() => {
+ openUserCountsBroadcast();
+ });
+
+ it('creates BroadcastChannel that updates DOM on message received', () => {
+ expect(findMRCountText()).toEqual('0');
+
+ channelMock.onmessage({ data: TEST_COUNT });
+
+ expect(findMRCountText()).toEqual(TEST_COUNT.toLocaleString());
+ });
+
+ it('closes if called while already open', () => {
+ expect(channelMock.close).not.toHaveBeenCalled();
+
+ openUserCountsBroadcast();
+
+ expect(channelMock.close).toHaveBeenCalled();
+ });
+ });
+
+ describe('closeUserCountsBroadcast', () => {
+ describe('when not opened', () => {
+ it('does nothing', () => {
+ expect(channelMock.close).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when opened', () => {
+ beforeEach(() => {
+ openUserCountsBroadcast();
+ });
+
+ it('closes', () => {
+ expect(channelMock.close).not.toHaveBeenCalled();
+
+ closeUserCountsBroadcast();
+
+ expect(channelMock.close).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap
new file mode 100644
index 00000000000..fd307ce5ab3
--- /dev/null
+++ b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap
@@ -0,0 +1,67 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Confidential merge request project form group component renders empty state when response is empty 1`] = `
+<div
+ class="form-group"
+>
+ <label>
+ Project
+ </label>
+
+ <div>
+ <!---->
+
+ <p
+ class="text-muted mt-1 mb-0"
+ >
+
+ No forks available to you.
+ <br />
+
+ <span>
+ To protect this issue's confidentiality,
+ <a
+ class="help-link"
+ href="https://test.com"
+ >
+ fork the project
+ </a>
+ and set the forks visiblity to private.
+ </span>
+ </p>
+ </div>
+</div>
+`;
+
+exports[`Confidential merge request project form group component renders fork dropdown 1`] = `
+<div
+ class="form-group"
+>
+ <label>
+ Project
+ </label>
+
+ <div>
+ <!---->
+
+ <p
+ class="text-muted mt-1 mb-0"
+ >
+
+ No forks available to you.
+ <br />
+
+ <span>
+ To protect this issue's confidentiality,
+ <a
+ class="help-link"
+ href="https://test.com"
+ >
+ fork the project
+ </a>
+ and set the forks visiblity to private.
+ </span>
+ </p>
+ </div>
+</div>
+`;
diff --git a/spec/frontend/confidential_merge_request/components/dropdown_spec.js b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
new file mode 100644
index 00000000000..69495f3c161
--- /dev/null
+++ b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
@@ -0,0 +1,56 @@
+import { mount } from '@vue/test-utils';
+import { GlDropdownItem } from '@gitlab/ui';
+import Dropdown from '~/confidential_merge_request/components/dropdown.vue';
+
+let vm;
+
+function factory(projects = []) {
+ vm = mount(Dropdown, {
+ propsData: {
+ projects,
+ selectedProject: projects[0],
+ },
+ });
+}
+
+describe('Confidential merge request project dropdown component', () => {
+ afterEach(() => {
+ vm.destroy();
+ });
+
+ it('renders dropdown items', () => {
+ factory([
+ {
+ id: 1,
+ name: 'test',
+ },
+ {
+ id: 2,
+ name: 'test',
+ },
+ ]);
+
+ expect(vm.findAll(GlDropdownItem).length).toBe(2);
+ });
+
+ it('renders selected project icon', () => {
+ factory([
+ {
+ id: 1,
+ name: 'test',
+ },
+ {
+ id: 2,
+ name: 'test 2',
+ },
+ ]);
+
+ expect(vm.find('.js-active-project-check').classes()).not.toContain('icon');
+ expect(
+ vm
+ .findAll('.js-active-project-check')
+ .at(1)
+ .classes(),
+ ).toContain('icon');
+ });
+});
diff --git a/spec/frontend/confidential_merge_request/components/project_form_group_spec.js b/spec/frontend/confidential_merge_request/components/project_form_group_spec.js
new file mode 100644
index 00000000000..3001363f7b9
--- /dev/null
+++ b/spec/frontend/confidential_merge_request/components/project_form_group_spec.js
@@ -0,0 +1,77 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import ProjectFormGroup from '~/confidential_merge_request/components/project_form_group.vue';
+
+const localVue = createLocalVue();
+const mockData = [
+ {
+ id: 1,
+ name_with_namespace: 'root / gitlab-ce',
+ path_with_namespace: 'root/gitlab-ce',
+ namespace: {
+ full_path: 'root',
+ },
+ },
+ {
+ id: 2,
+ name_with_namespace: 'test / gitlab-ce',
+ path_with_namespace: 'test/gitlab-ce',
+ namespace: {
+ full_path: 'test',
+ },
+ },
+];
+let vm;
+let mock;
+
+function factory(projects = mockData) {
+ mock = new MockAdapter(axios);
+ mock.onGet(/api\/(.*)\/projects\/gitlab-org%2Fgitlab-ce\/forks/).reply(200, projects);
+
+ vm = shallowMount(ProjectFormGroup, {
+ localVue,
+ propsData: {
+ namespacePath: 'gitlab-org',
+ projectPath: 'gitlab-org/gitlab-ce',
+ newForkPath: 'https://test.com',
+ helpPagePath: '/help',
+ },
+ });
+}
+
+describe('Confidential merge request project form group component', () => {
+ afterEach(() => {
+ mock.restore();
+ vm.destroy();
+ });
+
+ it('renders fork dropdown', () => {
+ factory();
+
+ return localVue.nextTick(() => {
+ expect(vm.element).toMatchSnapshot();
+ });
+ });
+
+ it('sets selected project as first fork', () => {
+ factory();
+
+ return localVue.nextTick(() => {
+ expect(vm.vm.selectedProject).toEqual({
+ id: 1,
+ name: 'root / gitlab-ce',
+ pathWithNamespace: 'root/gitlab-ce',
+ namespaceFullpath: 'root',
+ });
+ });
+ });
+
+ it('renders empty state when response is empty', () => {
+ factory([]);
+
+ return localVue.nextTick(() => {
+ expect(vm.element).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/frontend/create_merge_request_dropdown_spec.js
index 00fe3f451f5..6e41fdabdce 100644
--- a/spec/javascripts/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/create_merge_request_dropdown_spec.js
@@ -1,7 +1,7 @@
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import CreateMergeRequestDropdown from '~/create_merge_request_dropdown';
-import { TEST_HOST } from 'spec/test_constants';
+import { TEST_HOST } from './helpers/test_constants';
describe('CreateMergeRequestDropdown', () => {
let axiosMock;
@@ -10,7 +10,7 @@ describe('CreateMergeRequestDropdown', () => {
beforeEach(() => {
axiosMock = new MockAdapter(axios);
- setFixtures(`
+ document.body.innerHTML = `
<div id="dummy-wrapper-element">
<div class="available"></div>
<div class="unavailable">
@@ -18,11 +18,12 @@ describe('CreateMergeRequestDropdown', () => {
<div class="text"></div>
</div>
<div class="js-ref"></div>
+ <div class="js-create-mr"></div>
<div class="js-create-merge-request"></div>
<div class="js-create-target"></div>
<div class="js-dropdown-toggle"></div>
</div>
- `);
+ `;
const dummyElement = document.getElementById('dummy-wrapper-element');
dropdown = new CreateMergeRequestDropdown(dummyElement);
@@ -36,7 +37,7 @@ describe('CreateMergeRequestDropdown', () => {
describe('getRef', () => {
it('escapes branch names correctly', done => {
const endpoint = `${dropdown.refsPath}contains%23hash`;
- spyOn(axios, 'get').and.callThrough();
+ jest.spyOn(axios, 'get');
axiosMock.onGet(endpoint).replyOnce({});
dropdown
diff --git a/spec/frontend/ide/lib/files_spec.js b/spec/frontend/ide/lib/files_spec.js
index aa1fa0373db..08a31318544 100644
--- a/spec/frontend/ide/lib/files_spec.js
+++ b/spec/frontend/ide/lib/files_spec.js
@@ -12,6 +12,7 @@ const createEntries = paths => {
const { name, parent } = splitParent(path);
const parentEntry = acc[parent];
+ const previewMode = viewerInformationForPath(name);
acc[path] = {
...decorateData({
@@ -22,7 +23,8 @@ const createEntries = paths => {
path,
url: createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}/-/${escapeFileUrl(path)}`),
type,
- previewMode: viewerInformationForPath(path),
+ previewMode,
+ binary: (previewMode && previewMode.binary) || false,
parentPath: parent,
parentTreeUrl: parentEntry
? parentEntry.url
diff --git a/spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap b/spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap
new file mode 100644
index 00000000000..5f24bab600c
--- /dev/null
+++ b/spec/frontend/monitoring/__snapshots__/dashboard_state_spec.js.snap
@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EmptyState shows gettingStarted state 1`] = `
+<glemptystate-stub
+ description="Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
+ primarybuttonlink="/clustersPath"
+ primarybuttontext="Install on clusters"
+ secondarybuttonlink="/settingsPath"
+ secondarybuttontext="Configure existing installation"
+ svgpath="/path/to/getting-started.svg"
+ title="Get started with performance monitoring"
+/>
+`;
+
+exports[`EmptyState shows loading state 1`] = `
+<glemptystate-stub
+ description="Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
+ primarybuttonlink="/documentationPath"
+ primarybuttontext="View documentation"
+ secondarybuttonlink=""
+ secondarybuttontext=""
+ svgpath="/path/to/loading.svg"
+ title="Waiting for performance data"
+/>
+`;
+
+exports[`EmptyState shows unableToConnect state 1`] = `
+<glemptystate-stub
+ description="Ensure connectivity is available from the GitLab server to the Prometheus server"
+ primarybuttonlink="/documentationPath"
+ primarybuttontext="View documentation"
+ secondarybuttonlink="/settingsPath"
+ secondarybuttontext="Configure Prometheus"
+ svgpath="/path/to/unable-to-connect.svg"
+ title="Unable to connect to Prometheus server"
+/>
+`;
diff --git a/spec/frontend/monitoring/dashboard_state_spec.js b/spec/frontend/monitoring/dashboard_state_spec.js
new file mode 100644
index 00000000000..950422911eb
--- /dev/null
+++ b/spec/frontend/monitoring/dashboard_state_spec.js
@@ -0,0 +1,43 @@
+import { shallowMount } from '@vue/test-utils';
+import EmptyState from '~/monitoring/components/empty_state.vue';
+
+function createComponent(props) {
+ return shallowMount(EmptyState, {
+ propsData: {
+ ...props,
+ settingsPath: '/settingsPath',
+ clustersPath: '/clustersPath',
+ documentationPath: '/documentationPath',
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ },
+ });
+}
+
+describe('EmptyState', () => {
+ it('shows gettingStarted state', () => {
+ const wrapper = createComponent({
+ selectedState: 'gettingStarted',
+ });
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it('shows loading state', () => {
+ const wrapper = createComponent({
+ selectedState: 'loading',
+ });
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it('shows unableToConnect state', () => {
+ const wrapper = createComponent({
+ selectedState: 'unableToConnect',
+ });
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js b/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js
new file mode 100644
index 00000000000..279ca017b44
--- /dev/null
+++ b/spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js
@@ -0,0 +1,51 @@
+import { mount } from '@vue/test-utils';
+import DiscussionNotesRepliesWrapper from '~/notes/components/discussion_notes_replies_wrapper.vue';
+
+const TEST_CHILDREN = '<li>Hello!</li><li>World!</li>';
+
+// We have to wrap our SUT with a TestComponent because multiple roots are possible
+// because it's a functional component.
+const TestComponent = {
+ components: { DiscussionNotesRepliesWrapper },
+ template: `<ul><discussion-notes-replies-wrapper v-bind="$attrs">${TEST_CHILDREN}</discussion-notes-replies-wrapper></ul>`,
+};
+
+describe('DiscussionNotesRepliesWrapper', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = mount(TestComponent, {
+ propsData: props,
+ sync: false,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when normal discussion', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders children directly', () => {
+ expect(wrapper.html()).toEqual(`<ul>${TEST_CHILDREN}</ul>`);
+ });
+ });
+
+ describe('when diff discussion', () => {
+ beforeEach(() => {
+ createComponent({
+ isDiffDiscussion: true,
+ });
+ });
+
+ it('wraps children with notes', () => {
+ const notes = wrapper.find('li.discussion-collapsible ul.notes');
+
+ expect(notes.exists()).toBe(true);
+ expect(notes.html()).toEqual(`<ul class="notes">${TEST_CHILDREN}</ul>`);
+ });
+ });
+});
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index d36e428a8ee..93b86b9b812 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -21,6 +21,10 @@ describe GitlabSchema do
expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::Present::Instrumentation))
end
+ it 'enables using gitaly call checker' do
+ expect(field_instrumenters).to include(instance_of(::Gitlab::Graphql::CallsGitaly::Instrumentation))
+ end
+
it 'has the base mutation' do
expect(described_class.mutation).to eq(::Types::MutationType.to_graphql)
end
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index 0d3c3e37daf..77ef8933717 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -22,6 +22,24 @@ describe Types::BaseField do
expect(field.to_graphql.complexity).to eq 1
end
+ describe '#base_complexity' do
+ context 'with no gitaly calls' do
+ it 'defaults to 1' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true)
+
+ expect(field.base_complexity).to eq 1
+ end
+ end
+
+ context 'with a gitaly call' do
+ it 'adds 1 to the default value' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true)
+
+ expect(field.base_complexity).to eq 2
+ end
+ end
+ end
+
it 'has specified value' do
field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
@@ -52,5 +70,46 @@ describe Types::BaseField do
end
end
end
+
+ context 'calls_gitaly' do
+ context 'for fields with a resolver' do
+ it 'adds 1 if true' do
+ with_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true, calls_gitaly: true)
+ without_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true)
+ base_result = without_gitaly_field.to_graphql.complexity.call({}, {}, 2)
+
+ expect(with_gitaly_field.to_graphql.complexity.call({}, {}, 2)).to eq base_result + 1
+ end
+ end
+
+ context 'for fields without a resolver' do
+ it 'adds 1 if true' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true)
+
+ expect(field.to_graphql.complexity).to eq 2
+ end
+ end
+
+ it 'defaults to false' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true)
+
+ expect(field.base_complexity).to eq Types::BaseField::DEFAULT_COMPLEXITY
+ end
+
+ context 'with declared constant complexity value' do
+ it 'has complexity set to that constant' do
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
+
+ expect(field.to_graphql.complexity).to eq 12
+ end
+
+ it 'does not raise an error even with Gitaly calls' do
+ allow(Gitlab::GitalyClient).to receive(:get_request_count).and_return([0, 1])
+ field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
+
+ expect(field.to_graphql.complexity).to eq 12
+ end
+ end
+ end
end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index db0d45c3692..554c08add2d 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -28,7 +28,7 @@ describe PreferencesHelper do
["Your Projects' Activity", 'project_activity'],
["Starred Projects' Activity", 'starred_project_activity'],
["Your Groups", 'groups'],
- ["Your Todos", 'todos'],
+ ["Your To-Do List", 'todos'],
["Assigned Issues", 'issues'],
["Assigned Merge Requests", 'merge_requests']
]
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index 62c00964524..4bd0fbb76ca 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -31,7 +31,7 @@ describe StorageHelper do
build_artifacts_size: 30.megabytes))
end
- let(:message) { '10 KB repositories, 10 Bytes wikis, 30 MB build artifacts, 20 GB LFS' }
+ let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / LFS: 20 GB' }
it 'works on ProjectStatistics' do
expect(helper.storage_counters_details(project.statistics)).to eq(message)
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index 9854cf49e97..ea22ae5c4e7 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -1,4 +1,5 @@
import BoardService from '~/boards/services/board_service';
+import boardsStore from '~/boards/stores/boards_store';
export const boardObj = {
id: 1,
@@ -76,12 +77,14 @@ export const mockBoardService = (opts = {}) => {
const bulkUpdatePath = opts.bulkUpdatePath || '';
const boardId = opts.boardId || '1';
- return new BoardService({
+ boardsStore.setEndpoints({
boardsEndpoint,
listsEndpoint,
bulkUpdatePath,
boardId,
});
+
+ return new BoardService();
};
export const mockAssigneesList = [
diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js
index bb90e53e525..f75d63c8f57 100644
--- a/spec/javascripts/collapsed_sidebar_todo_spec.js
+++ b/spec/javascripts/collapsed_sidebar_todo_spec.js
@@ -58,7 +58,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
it('sets default tooltip title', () => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('title'),
- ).toBe('Add todo');
+ ).toBe('Add a To Do');
});
it('toggle todo state', done => {
@@ -85,7 +85,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
setTimeout(() => {
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
- ).toBe('Mark todo as done');
+ ).toBe('Mark as done');
done();
});
@@ -99,7 +99,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('data-original-title'),
- ).toBe('Mark todo as done');
+ ).toBe('Mark as done');
done();
});
@@ -124,13 +124,13 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
- ).toBe('Add todo');
+ ).toBe('Add a To Do');
})
.then(done)
.catch(done.fail);
});
- it('updates aria-label to mark todo as done', done => {
+ it('updates aria-label to Mark as done', done => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setTimeout(() => {
@@ -138,7 +138,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
- ).toBe('Mark todo as done');
+ ).toBe('Mark as done');
done();
});
@@ -153,7 +153,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
- ).toBe('Mark todo as done');
+ ).toBe('Mark as done');
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
})
@@ -163,7 +163,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
- ).toBe('Add todo');
+ ).toBe('Add a To Do');
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index 0b3890b68d6..9b61dbe7975 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -10,6 +10,7 @@ describe('InlineDiffView', () => {
let component;
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+ const notesLength = getDiscussionsMockData()[0].notes.length;
beforeEach(done => {
const diffFile = getDiffFileMock();
@@ -40,7 +41,7 @@ describe('InlineDiffView', () => {
Vue.nextTick(() => {
expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
- expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(6);
+ expect(el.querySelectorAll('.notes_holder .note').length).toEqual(notesLength + 1);
expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1);
component.$store.dispatch('setInitialNotes', []);
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index f832096701f..7dc5cb24981 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -30,6 +30,7 @@ describe('RepoEditor', () => {
Vue.set(vm.$store.state.entries, f.path, f);
spyOn(vm, 'getFileData').and.returnValue(Promise.resolve());
+ spyOn(vm, 'getRawFileData').and.returnValue(Promise.resolve());
vm.$mount();
@@ -407,6 +408,44 @@ describe('RepoEditor', () => {
});
});
+ describe('initEditor', () => {
+ beforeEach(() => {
+ spyOn(vm.editor, 'createInstance');
+ spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
+ });
+
+ it('is being initialised for files without content even if shouldHideEditor is `true`', done => {
+ vm.file.content = '';
+ vm.file.raw = '';
+
+ vm.initEditor();
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.getFileData).toHaveBeenCalled();
+ expect(vm.getRawFileData).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not initialize editor for files already with content', done => {
+ expect(vm.getFileData.calls.count()).toEqual(1);
+ expect(vm.getRawFileData.calls.count()).toEqual(1);
+
+ vm.file.content = 'foo';
+
+ vm.initEditor();
+ vm.$nextTick()
+ .then(() => {
+ expect(vm.getFileData.calls.count()).toEqual(1);
+ expect(vm.getRawFileData.calls.count()).toEqual(1);
+ expect(vm.editor.createInstance).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
it('calls removePendingTab when old file is pending', done => {
spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
spyOn(vm, 'removePendingTab');
@@ -416,6 +455,7 @@ describe('RepoEditor', () => {
vm.$nextTick()
.then(() => {
vm.file = file('testing');
+ vm.file.content = 'foo'; // need to prevent full cycle of initEditor
return vm.$nextTick();
})
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 18ee4330f69..7714f66c9a4 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -83,6 +83,26 @@ describe('IDE store file mutations', () => {
expect(localFile.raw).toBeNull();
expect(localFile.baseRaw).toBeNull();
});
+
+ it('sets extra file data to all arrays concerned', () => {
+ localState.stagedFiles = [localFile];
+ localState.changedFiles = [localFile];
+ localState.openFiles = [localFile];
+
+ const rawPath = 'foo/bar/blah.md';
+
+ mutations.SET_FILE_DATA(localState, {
+ data: {
+ raw_path: rawPath,
+ },
+ file: localFile,
+ });
+
+ expect(localState.stagedFiles[0].rawPath).toEqual(rawPath);
+ expect(localState.changedFiles[0].rawPath).toEqual(rawPath);
+ expect(localState.openFiles[0].rawPath).toEqual(rawPath);
+ expect(localFile.rawPath).toEqual(rawPath);
+ });
});
describe('SET_FILE_RAW_DATA', () => {
diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js
index 25543053eba..4d57bfb1b33 100644
--- a/spec/javascripts/issuable_spec.js
+++ b/spec/javascripts/issuable_spec.js
@@ -2,14 +2,14 @@ import $ from 'jquery';
import MockAdaptor from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import IssuableIndex from '~/issuable_index';
+import issuableInitBulkUpdateSidebar from '~/issuable_init_bulk_update_sidebar';
describe('Issuable', () => {
- let Issuable;
describe('initBulkUpdate', () => {
it('should not set bulkUpdateSidebar', () => {
- Issuable = new IssuableIndex('issue_');
+ new IssuableIndex('issue_'); // eslint-disable-line no-new
- expect(Issuable.bulkUpdateSidebar).not.toBeDefined();
+ expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeNull();
});
it('should set bulkUpdateSidebar', () => {
@@ -17,9 +17,9 @@ describe('Issuable', () => {
element.classList.add('issues-bulk-update');
document.body.appendChild(element);
- Issuable = new IssuableIndex('issue_');
+ new IssuableIndex('issue_'); // eslint-disable-line no-new
- expect(Issuable.bulkUpdateSidebar).toBeDefined();
+ expect(issuableInitBulkUpdateSidebar.bulkUpdateSidebar).toBeDefined();
});
});
@@ -36,7 +36,7 @@ describe('Issuable', () => {
input.setAttribute('id', 'issuable_email');
document.body.appendChild(input);
- Issuable = new IssuableIndex('issue_');
+ new IssuableIndex('issue_'); // eslint-disable-line no-new
mock = new MockAdaptor(axios);
diff --git a/spec/javascripts/monitoring/dashboard_state_spec.js b/spec/javascripts/monitoring/dashboard_state_spec.js
deleted file mode 100644
index 6b2be83aa8c..00000000000
--- a/spec/javascripts/monitoring/dashboard_state_spec.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import Vue from 'vue';
-import EmptyState from '~/monitoring/components/empty_state.vue';
-import { statePaths } from './mock_data';
-
-function createComponent(props) {
- const Component = Vue.extend(EmptyState);
-
- return new Component({
- propsData: {
- ...props,
- settingsPath: statePaths.settingsPath,
- clustersPath: statePaths.clustersPath,
- documentationPath: statePaths.documentationPath,
- emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
- emptyLoadingSvgPath: '/path/to/loading.svg',
- emptyNoDataSvgPath: '/path/to/no-data.svg',
- emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
- },
- }).$mount();
-}
-
-function getTextFromNode(component, selector) {
- return component.$el.querySelector(selector).firstChild.nodeValue.trim();
-}
-
-describe('EmptyState', () => {
- describe('Computed props', () => {
- it('currentState', () => {
- const component = createComponent({
- selectedState: 'gettingStarted',
- });
-
- expect(component.currentState).toBe(component.states.gettingStarted);
- });
-
- it('showButtonDescription returns a description with a link for the unableToConnect state', () => {
- const component = createComponent({
- selectedState: 'unableToConnect',
- });
-
- expect(component.showButtonDescription).toEqual(true);
- });
-
- it('showButtonDescription returns the description without a link for any other state', () => {
- const component = createComponent({
- selectedState: 'loading',
- });
-
- expect(component.showButtonDescription).toEqual(false);
- });
- });
-
- it('should show the gettingStarted state', () => {
- const component = createComponent({
- selectedState: 'gettingStarted',
- });
-
- expect(component.$el.querySelector('svg')).toBeDefined();
- expect(getTextFromNode(component, '.state-title')).toEqual(
- component.states.gettingStarted.title,
- );
-
- expect(getTextFromNode(component, '.state-description')).toEqual(
- component.states.gettingStarted.description,
- );
-
- expect(getTextFromNode(component, '.btn-success')).toEqual(
- component.states.gettingStarted.buttonText,
- );
- });
-
- it('should show the loading state', () => {
- const component = createComponent({
- selectedState: 'loading',
- });
-
- expect(component.$el.querySelector('svg')).toBeDefined();
- expect(getTextFromNode(component, '.state-title')).toEqual(component.states.loading.title);
- expect(getTextFromNode(component, '.state-description')).toEqual(
- component.states.loading.description,
- );
-
- expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.loading.buttonText);
- });
-
- it('should show the unableToConnect state', () => {
- const component = createComponent({
- selectedState: 'unableToConnect',
- });
-
- expect(component.$el.querySelector('svg')).toBeDefined();
- expect(getTextFromNode(component, '.state-title')).toEqual(
- component.states.unableToConnect.title,
- );
-
- expect(component.$el.querySelector('.state-description a')).toBeDefined();
- expect(getTextFromNode(component, '.btn-success')).toEqual(
- component.states.unableToConnect.buttonText,
- );
- });
-});
diff --git a/spec/javascripts/monitoring/store/utils_spec.js b/spec/javascripts/monitoring/store/utils_spec.js
new file mode 100644
index 00000000000..73dd370ffb3
--- /dev/null
+++ b/spec/javascripts/monitoring/store/utils_spec.js
@@ -0,0 +1,37 @@
+import { groupQueriesByChartInfo } from '~/monitoring/stores/utils';
+
+describe('groupQueriesByChartInfo', () => {
+ let input;
+ let output;
+
+ it('groups metrics with the same chart title and y_axis label', () => {
+ input = [
+ { title: 'title', y_label: 'MB', queries: [{}] },
+ { title: 'title', y_label: 'MB', queries: [{}] },
+ { title: 'new title', y_label: 'MB', queries: [{}] },
+ ];
+
+ output = [
+ { title: 'title', y_label: 'MB', queries: [{ metricId: null }, { metricId: null }] },
+ { title: 'new title', y_label: 'MB', queries: [{ metricId: null }] },
+ ];
+
+ expect(groupQueriesByChartInfo(input)).toEqual(output);
+ });
+
+ // Functionality associated with the /additional_metrics endpoint
+ it("associates a chart's stringified metric_id with the metric", () => {
+ input = [{ id: 3, title: 'new title', y_label: 'MB', queries: [{}] }];
+ output = [{ id: 3, title: 'new title', y_label: 'MB', queries: [{ metricId: '3' }] }];
+
+ expect(groupQueriesByChartInfo(input)).toEqual(output);
+ });
+
+ // Functionality associated with the /metrics_dashboard endpoint
+ it('aliases a stringified metrics_id on the metric to the metricId key', () => {
+ input = [{ title: 'new title', y_label: 'MB', queries: [{ metric_id: 3 }] }];
+ output = [{ title: 'new title', y_label: 'MB', queries: [{ metricId: '3', metric_id: 3 }] }];
+
+ expect(groupQueriesByChartInfo(input)).toEqual(output);
+ });
+});
diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js
index 362963ddaf4..88c86746992 100644
--- a/spec/javascripts/notes/components/comment_form_spec.js
+++ b/spec/javascripts/notes/components/comment_form_spec.js
@@ -251,6 +251,21 @@ describe('issue_comment_form component', () => {
});
});
});
+
+ describe('when toggling state', () => {
+ it('should update MR count', done => {
+ spyOn(vm, 'closeIssue').and.returnValue(Promise.resolve());
+
+ const updateMrCountSpy = spyOnDependency(CommentForm, 'refreshUserMergeRequestCounts');
+ vm.toggleIssueState();
+
+ Vue.nextTick(() => {
+ expect(updateMrCountSpy).toHaveBeenCalled();
+
+ done();
+ });
+ });
+ });
});
describe('issue is confidential', () => {
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 65f72a135aa..c97ff5236ec 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import $ from 'jquery';
import _ from 'underscore';
+import Api from '~/api';
import { TEST_HOST } from 'spec/test_constants';
import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
import actionsModule, * as actions from '~/notes/stores/actions';
@@ -8,7 +9,6 @@ import * as mutationTypes from '~/notes/stores/mutation_types';
import * as notesConstants from '~/notes/constants';
import createStore from '~/notes/stores';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
-import service from '~/notes/services/notes_service';
import testAction from '../../helpers/vuex_action_helper';
import { resetStore } from '../helpers';
import {
@@ -846,9 +846,9 @@ describe('Actions Notes Store', () => {
let flashContainer;
beforeEach(() => {
- spyOn(service, 'applySuggestion');
+ spyOn(Api, 'applySuggestion');
dispatch.and.returnValue(Promise.resolve());
- service.applySuggestion.and.returnValue(Promise.resolve());
+ Api.applySuggestion.and.returnValue(Promise.resolve());
flashContainer = {};
});
@@ -877,7 +877,7 @@ describe('Actions Notes Store', () => {
it('when service fails, flashes error message', done => {
const response = { response: { data: { message: TEST_ERROR_MESSAGE } } };
- service.applySuggestion.and.returnValue(Promise.reject(response));
+ Api.applySuggestion.and.returnValue(Promise.reject(response));
testSubmitSuggestion(done, () => {
expect(commit).not.toHaveBeenCalled();
diff --git a/spec/javascripts/sidebar/todo_spec.js b/spec/javascripts/sidebar/todo_spec.js
index f46ea5a0499..e7abd19c865 100644
--- a/spec/javascripts/sidebar/todo_spec.js
+++ b/spec/javascripts/sidebar/todo_spec.js
@@ -53,14 +53,14 @@ describe('SidebarTodo', () => {
describe('buttonLabel', () => {
it('returns todo button text for marking todo as done when `isTodo` prop is `true`', () => {
- expect(vm.buttonLabel).toBe('Mark todo as done');
+ expect(vm.buttonLabel).toBe('Mark as done');
});
it('returns todo button text for add todo when `isTodo` prop is `false`', done => {
vm.isTodo = false;
Vue.nextTick()
.then(() => {
- expect(vm.buttonLabel).toBe('Add todo');
+ expect(vm.buttonLabel).toBe('Add a To Do');
})
.then(done)
.catch(done.fail);
@@ -131,14 +131,14 @@ describe('SidebarTodo', () => {
});
it('check button label computed property', () => {
- expect(vm.buttonLabel).toEqual('Mark todo as done');
+ expect(vm.buttonLabel).toEqual('Mark as done');
});
it('renders button label element when `collapsed` prop is `false`', () => {
const buttonLabelEl = vm.$el.querySelector('span.issuable-todo-inner');
expect(buttonLabelEl).not.toBeNull();
- expect(buttonLabelEl.innerText.trim()).toBe('Mark todo as done');
+ expect(buttonLabelEl.innerText.trim()).toBe('Mark as done');
});
it('renders button icon when `collapsed` prop is `true`', done => {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 2cc476ed52a..50741e249ca 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -8,7 +8,6 @@ import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate';
-import CheckEE from '~/vue_shared/mixins/is_ee';
import jasmineDiff from 'jasmine-diff';
import { config as testUtilsConfig } from '@vue/test-utils';
@@ -48,7 +47,6 @@ Vue.config.errorHandler = function(err) {
Vue.use(VueResource);
Vue.use(Translate);
-Vue.use(CheckEE);
// enable test fixtures
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index bb76616be56..ba3ba01944d 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -58,9 +58,11 @@ const createComponent = (customConfig = {}) => {
describe('ReadyToMerge', () => {
let vm;
+ let updateMrCountSpy;
beforeEach(() => {
vm = createComponent();
+ updateMrCountSpy = spyOnDependency(ReadyToMerge, 'refreshUserMergeRequestCounts');
});
afterEach(() => {
@@ -461,6 +463,7 @@ describe('ReadyToMerge', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
expect(eventHub.$emit).toHaveBeenCalledWith('FetchActionsContent');
expect(vm.initiateRemoveSourceBranchPolling).toHaveBeenCalled();
+ expect(updateMrCountSpy).toHaveBeenCalled();
expect(cpc).toBeFalsy();
expect(spc).toBeTruthy();
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 403e0785d1b..127463a57e8 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -144,6 +144,68 @@ describe Feature do
expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
end
+ it { expect(described_class.l1_cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend) }
+ it { expect(described_class.l2_cache_backend).to eq(Rails.cache) }
+
+ it 'caches the status in L1 and L2 caches',
+ :request_store, :use_clean_rails_memory_store_caching do
+ described_class.enable(:enabled_feature_flag)
+ flipper_key = "flipper/v1/feature/enabled_feature_flag"
+
+ expect(described_class.l2_cache_backend)
+ .to receive(:fetch)
+ .once
+ .with(flipper_key, expires_in: 1.hour)
+ .and_call_original
+
+ expect(described_class.l1_cache_backend)
+ .to receive(:fetch)
+ .once
+ .with(flipper_key, expires_in: 1.minute)
+ .and_call_original
+
+ 2.times do
+ expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
+ end
+ end
+
+ context 'cached feature flag', :request_store do
+ let(:flag) { :some_feature_flag }
+
+ before do
+ described_class.flipper.memoize = false
+ described_class.enabled?(flag)
+ end
+
+ it 'caches the status in L1 cache for the first minute' do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).not_to receive(:fetch)
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(0)
+ end
+
+ it 'caches the status in L2 cache after 2 minutes' do
+ Timecop.travel 2.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(0)
+ end
+ end
+
+ it 'fetches the status after an hour' do
+ Timecop.travel 61.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).once.and_call_original
+ expect(described_class.enabled?(flag)).to be_truthy
+ end.not_to exceed_query_limit(1)
+ end
+ end
+ end
+
context 'with an individual actor' do
CustomActor = Struct.new(:flipper_id)
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index 483c5ea9cff..972dd7e0d2b 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -26,6 +26,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do
end
it 'loads 10 projects without hitting Gitaly call limit', :request_store do
+ allow(Gitlab::GitalyClient).to receive(:can_access_disk?).and_return(false)
projects = Gitlab::GitalyClient.allow_n_plus_1_calls do
(1..10).map { create(:project, :repository) }
end
diff --git a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
index 71c61e0345f..36582204cc1 100644
--- a/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb
@@ -74,17 +74,11 @@ describe Gitlab::Ci::Reports::TestReportsComparer do
subject { comparer.resolved_count }
context 'when there is a resolved test case in head suites' do
- let(:create_test_case_java_resolved) do
- create_test_case_java_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
- end
- end
-
before do
base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
- head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
end
it 'returns the correct count' do
diff --git a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
index 6ab16e5518d..579b3e6fd24 100644
--- a/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb
@@ -10,12 +10,6 @@ describe Gitlab::Ci::Reports::TestSuiteComparer do
let(:test_case_success) { create_test_case_rspec_success }
let(:test_case_failed) { create_test_case_rspec_failed }
- let(:test_case_resolved) do
- create_test_case_rspec_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
- end
- end
-
describe '#new_failures' do
subject { comparer.new_failures }
@@ -44,7 +38,7 @@ describe Gitlab::Ci::Reports::TestSuiteComparer do
context 'when head sutie has a success test case which failed in base' do
before do
base_suite.add_test_case(test_case_failed)
- head_suite.add_test_case(test_case_resolved)
+ head_suite.add_test_case(test_case_success)
end
it 'does not return the failed test case' do
@@ -81,7 +75,7 @@ describe Gitlab::Ci::Reports::TestSuiteComparer do
context 'when head sutie has a success test case which failed in base' do
before do
base_suite.add_test_case(test_case_failed)
- head_suite.add_test_case(test_case_resolved)
+ head_suite.add_test_case(test_case_success)
end
it 'does not return the failed test case' do
@@ -126,11 +120,11 @@ describe Gitlab::Ci::Reports::TestSuiteComparer do
context 'when head sutie has a success test case which failed in base' do
before do
base_suite.add_test_case(test_case_failed)
- head_suite.add_test_case(test_case_resolved)
+ head_suite.add_test_case(test_case_success)
end
it 'does not return the resolved test case' do
- is_expected.to eq([test_case_resolved])
+ is_expected.to eq([test_case_success])
end
it 'returns the correct resolved count' do
@@ -156,13 +150,8 @@ describe Gitlab::Ci::Reports::TestSuiteComparer do
context 'when there are a new failure and an existing failure' do
let(:test_case_1_success) { create_test_case_rspec_success }
- let(:test_case_2_failed) { create_test_case_rspec_failed }
-
- let(:test_case_1_failed) do
- create_test_case_rspec_success.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
- end
- end
+ let(:test_case_1_failed) { create_test_case_rspec_failed }
+ let(:test_case_2_failed) { create_test_case_rspec_failed('case2') }
before do
base_suite.add_test_case(test_case_1_success)
diff --git a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
index eacde22cd56..8633a63849f 100644
--- a/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/test_stage_spec.rb
@@ -3,6 +3,40 @@ require 'lib/gitlab/cycle_analytics/shared_stage_spec'
describe Gitlab::CycleAnalytics::TestStage do
let(:stage_name) { :test }
+ let(:project) { create(:project) }
+ let(:stage) { described_class.new(project: project, options: { from: 2.days.ago, current_user: project.creator }) }
it_behaves_like 'base stage'
+
+ describe '#median' do
+ before do
+ issue_1 = create(:issue, project: project, created_at: 90.minutes.ago)
+ issue_2 = create(:issue, project: project, created_at: 60.minutes.ago)
+ issue_3 = create(:issue, project: project, created_at: 60.minutes.ago)
+ mr_1 = create(:merge_request, :closed, source_project: project, created_at: 60.minutes.ago)
+ mr_2 = create(:merge_request, :closed, source_project: project, created_at: 40.minutes.ago, source_branch: 'A')
+ mr_3 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'B')
+ mr_4 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'C')
+ mr_5 = create(:merge_request, source_project: project, created_at: 10.minutes.ago, source_branch: 'D')
+ mr_1.metrics.update!(latest_build_started_at: 32.minutes.ago, latest_build_finished_at: 2.minutes.ago)
+ mr_2.metrics.update!(latest_build_started_at: 62.minutes.ago, latest_build_finished_at: 32.minutes.ago)
+ mr_3.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil)
+ mr_4.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil)
+ mr_5.metrics.update!(latest_build_started_at: nil, latest_build_finished_at: nil)
+
+ create(:merge_requests_closing_issues, merge_request: mr_1, issue: issue_1)
+ create(:merge_requests_closing_issues, merge_request: mr_2, issue: issue_2)
+ create(:merge_requests_closing_issues, merge_request: mr_3, issue: issue_3)
+ create(:merge_requests_closing_issues, merge_request: mr_4, issue: issue_3)
+ create(:merge_requests_closing_issues, merge_request: mr_5, issue: issue_3)
+ end
+
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ it 'counts median from issues with metrics' do
+ expect(stage.median).to eq(ISSUES_MEDIAN)
+ end
+ end
end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index c8e65a3e59d..92d90ac2fef 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -87,13 +87,13 @@ describe Gitlab::Danger::Helper do
describe '#changes_by_category' do
it 'categorizes changed files' do
- expect(fake_git).to receive(:added_files) { %w[foo foo.md foo.rb foo.js db/foo qa/foo ee/changelogs/foo.yml] }
+ expect(fake_git).to receive(:added_files) { %w[foo foo.md foo.rb foo.js db/foo lib/gitlab/database/foo.rb qa/foo ee/changelogs/foo.yml] }
allow(fake_git).to receive(:modified_files) { [] }
allow(fake_git).to receive(:renamed_files) { [] }
expect(helper.changes_by_category).to eq(
backend: %w[foo.rb],
- database: %w[db/foo],
+ database: %w[db/foo lib/gitlab/database/foo.rb],
frontend: %w[foo.js],
none: %w[ee/changelogs/foo.yml foo.md],
qa: %w[qa/foo],
@@ -159,9 +159,22 @@ describe Gitlab::Danger::Helper do
'ee/FOO_VERSION' | :unknown
- 'db/foo' | :database
- 'qa/foo' | :qa
+ 'db/foo' | :database
+ 'ee/db/foo' | :database
+ 'app/models/project_authorization.rb' | :database
+ 'app/services/users/refresh_authorized_projects_service.rb' | :database
+ 'lib/gitlab/background_migration.rb' | :database
+ 'lib/gitlab/background_migration/foo' | :database
+ 'ee/lib/gitlab/background_migration/foo' | :database
+ 'lib/gitlab/database.rb' | :database
+ 'lib/gitlab/database/foo' | :database
+ 'ee/lib/gitlab/database/foo' | :database
+ 'lib/gitlab/github_import.rb' | :database
+ 'lib/gitlab/github_import/foo' | :database
+ 'lib/gitlab/sql/foo' | :database
+ 'rubocop/cop/migration/foo' | :database
+ 'qa/foo' | :qa
'ee/qa/foo' | :qa
'changelogs/foo' | :none
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index aea02d21048..b755cd1aff0 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -610,4 +610,17 @@ describe Gitlab::Diff::Position do
it_behaves_like "diff position json"
end
end
+
+ describe "#file_hash" do
+ subject do
+ described_class.new(
+ old_path: "image.jpg",
+ new_path: "image.jpg"
+ )
+ end
+
+ it "returns SHA1 representation of the file_path" do
+ expect(subject.file_hash).to eq(Digest::SHA1.hexdigest(subject.file_path))
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb b/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb
new file mode 100644
index 00000000000..900816af53a
--- /dev/null
+++ b/spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb
@@ -0,0 +1,238 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::PositionTracer::ImageStrategy do
+ include PositionTracerHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:current_user) { project.owner }
+ let(:file_name) { 'test-file' }
+ let(:new_file_name) { "#{file_name}-new" }
+ let(:second_file_name) { "#{file_name}-2" }
+ let(:branch_name) { 'position-tracer-test' }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, position_type: 'image') }
+
+ let(:tracer) do
+ Gitlab::Diff::PositionTracer.new(
+ project: project,
+ old_diff_refs: old_diff_refs,
+ new_diff_refs: new_diff_refs
+ )
+ end
+
+ let(:strategy) { described_class.new(tracer) }
+
+ subject { strategy.trace(old_position) }
+
+ let(:initial_commit) do
+ project.commit(create_branch(branch_name, 'master')[:branch].name)
+ end
+
+ describe '#trace' do
+ describe 'diff scenarios' do
+ let(:create_file_commit) do
+ initial_commit
+
+ create_file(
+ branch_name,
+ file_name,
+ Base64.encode64('content')
+ )
+ end
+
+ let(:update_file_commit) do
+ create_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ Base64.encode64('updatedcontent')
+ )
+ end
+
+ let(:update_file_again_commit) do
+ update_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ Base64.encode64('updatedcontentagain')
+ )
+ end
+
+ let(:delete_file_commit) do
+ create_file_commit
+ delete_file(branch_name, file_name)
+ end
+
+ let(:rename_file_commit) do
+ delete_file_commit
+
+ create_file(
+ branch_name,
+ new_file_name,
+ Base64.encode64('renamedcontent')
+ )
+ end
+
+ let(:create_second_file_commit) do
+ create_file_commit
+
+ create_file(
+ branch_name,
+ second_file_name,
+ Base64.encode64('morecontent')
+ )
+ end
+
+ let(:create_another_file_commit) do
+ create_file(
+ branch_name,
+ second_file_name,
+ Base64.encode64('morecontent')
+ )
+ end
+
+ let(:update_another_file_commit) do
+ update_file(
+ branch_name,
+ second_file_name,
+ Base64.encode64('updatedmorecontent')
+ )
+ end
+
+ context 'when the file was created in the old diff' do
+ context 'when the file is unchanged between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, create_second_file_commit) }
+
+ it 'returns the new position' do
+ expect_new_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was updated between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was renamed in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, rename_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, rename_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was removed in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, delete_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file is unchanged in the new diff' do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_another_file_commit, update_another_file_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, create_another_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+ end
+
+ context 'when the file was changed in the old diff' do
+ context 'when the file is unchanged in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, create_second_file_commit) }
+
+ it 'returns the new position' do
+ expect_new_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was updated in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was renamed in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, rename_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, rename_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file was removed in between the old and the new diff' do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, delete_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+
+ context 'when the file is unchanged in the new diff' do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_another_file_commit, update_another_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, create_another_file_commit) }
+
+ it 'returns the position of the change' do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb b/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
new file mode 100644
index 00000000000..7f4902c5b86
--- /dev/null
+++ b/spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb
@@ -0,0 +1,1805 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::PositionTracer::LineStrategy do
+ # Douwe's diary New York City, 2016-06-28
+ # --------------------------------------------------------------------------
+ #
+ # Dear diary,
+ #
+ # Ideally, we would have a test for every single diff scenario that can
+ # occur and that the PositionTracer should correctly trace a position
+ # through, across the following variables:
+ #
+ # - Old diff file type: created, changed, renamed, deleted, unchanged (5)
+ # - Old diff line type: added, removed, unchanged (3)
+ # - New diff file type: created, changed, renamed, deleted, unchanged (5)
+ # - New diff line type: added, removed, unchanged (3)
+ # - Old-to-new diff line change: kept, moved, undone (3)
+ #
+ # This adds up to 5 * 3 * 5 * 3 * 3 = 675 different potential scenarios,
+ # and 675 different tests to cover them all. In reality, it would be fewer,
+ # since one cannot have a removed line in a created file diff, for example,
+ # but for the sake of this diary entry, let's be pessimistic.
+ #
+ # Writing these tests is a manual and time consuming process, as every test
+ # requires the manual construction or finding of a combination of diffs that
+ # create the exact diff scenario we are looking for, and can take between
+ # 1 and 10 minutes, depending on the farfetchedness of the scenario and
+ # complexity of creating it.
+ #
+ # This means that writing tests to cover all of these scenarios would end up
+ # taking between 11 and 112 hours in total, which I do not believe is the
+ # best use of my time.
+ #
+ # A better course of action would be to think of scenarios that are likely
+ # to occur, but also potentially tricky to trace correctly, and only cover
+ # those, with a few more obvious scenarios thrown in to cover our bases.
+ #
+ # Unfortunately, I only came to the above realization once I was about
+ # 1/5th of the way through the process of writing ALL THE SPECS, having
+ # already wasted about 3 hours trying to be thorough.
+ #
+ # I did find 2 bugs while writing those though, so that's good.
+ #
+ # In any case, all of this means that the tests below will be extremely
+ # (excessively, unjustifiably) thorough for scenarios where "the file was
+ # created in the old diff" and then drop off to comparatively lackluster
+ # testing of other scenarios.
+ #
+ # I did still try to cover most of the obvious and potentially tricky
+ # scenarios, though.
+
+ include RepoHelpers
+ include PositionTracerHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:current_user) { project.owner }
+ let(:repository) { project.repository }
+ let(:file_name) { "test-file" }
+ let(:new_file_name) { "#{file_name}-new" }
+ let(:second_file_name) { "#{file_name}-2" }
+ let(:branch_name) { "position-tracer-test" }
+
+ let(:old_diff_refs) { raise NotImplementedError }
+ let(:new_diff_refs) { raise NotImplementedError }
+ let(:change_diff_refs) { raise NotImplementedError }
+ let(:old_position) { raise NotImplementedError }
+
+ let(:tracer) do
+ Gitlab::Diff::PositionTracer.new(
+ project: project,
+ old_diff_refs: old_diff_refs,
+ new_diff_refs: new_diff_refs
+ )
+ end
+
+ let(:strategy) { described_class.new(tracer) }
+
+ subject { strategy.trace(old_position) }
+
+ let(:initial_commit) do
+ project.commit(create_branch(branch_name, 'master')[:branch].name)
+ end
+
+ describe "#trace" do
+ describe "diff scenarios" do
+ let(:create_file_commit) do
+ initial_commit
+
+ create_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ B
+ C
+ CONTENT
+ )
+ end
+
+ let(:create_second_file_commit) do
+ create_file_commit
+
+ create_file(
+ branch_name,
+ second_file_name,
+ <<-CONTENT.strip_heredoc
+ D
+ E
+ CONTENT
+ )
+ end
+
+ let(:update_line_commit) do
+ create_second_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ BB
+ C
+ CONTENT
+ )
+ end
+
+ let(:update_second_file_line_commit) do
+ update_line_commit
+
+ update_file(
+ branch_name,
+ second_file_name,
+ <<-CONTENT.strip_heredoc
+ D
+ EE
+ CONTENT
+ )
+ end
+
+ let(:move_line_commit) do
+ update_second_file_line_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ BB
+ A
+ C
+ CONTENT
+ )
+ end
+
+ let(:add_second_file_line_commit) do
+ move_line_commit
+
+ update_file(
+ branch_name,
+ second_file_name,
+ <<-CONTENT.strip_heredoc
+ D
+ EE
+ F
+ CONTENT
+ )
+ end
+
+ let(:move_second_file_line_commit) do
+ add_second_file_line_commit
+
+ update_file(
+ branch_name,
+ second_file_name,
+ <<-CONTENT.strip_heredoc
+ D
+ F
+ EE
+ CONTENT
+ )
+ end
+
+ let(:delete_line_commit) do
+ move_second_file_line_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ BB
+ A
+ CONTENT
+ )
+ end
+
+ let(:delete_second_file_line_commit) do
+ delete_line_commit
+
+ update_file(
+ branch_name,
+ second_file_name,
+ <<-CONTENT.strip_heredoc
+ D
+ F
+ CONTENT
+ )
+ end
+
+ let(:delete_file_commit) do
+ delete_second_file_line_commit
+
+ delete_file(branch_name, file_name)
+ end
+
+ let(:rename_file_commit) do
+ delete_file_commit
+
+ create_file(
+ branch_name,
+ new_file_name,
+ <<-CONTENT.strip_heredoc
+ BB
+ A
+ CONTENT
+ )
+ end
+
+ let(:update_line_again_commit) do
+ rename_file_commit
+
+ update_file(
+ branch_name,
+ new_file_name,
+ <<-CONTENT.strip_heredoc
+ BB
+ AA
+ CONTENT
+ )
+ end
+
+ let(:move_line_again_commit) do
+ update_line_again_commit
+
+ update_file(
+ branch_name,
+ new_file_name,
+ <<-CONTENT.strip_heredoc
+ AA
+ BB
+ CONTENT
+ )
+ end
+
+ let(:delete_line_again_commit) do
+ move_line_again_commit
+
+ update_file(
+ branch_name,
+ new_file_name,
+ <<-CONTENT.strip_heredoc
+ AA
+ CONTENT
+ )
+ end
+
+ context "when the file was created in the old diff" do
+ context "when the file is created in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, create_second_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(update_line_commit, delete_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 3) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file is changed in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 3) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 - A
+ # 2 1 BB
+ # 2 + A
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 - A
+ # 2 1 BB
+ # 2 + A
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 3) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 BB
+ # 2 2 A
+ # 3 - C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file is renamed in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, rename_file_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ #
+ # new diff:
+ # file_name -> new_file_name
+ # 1 1 BB
+ # 2 2 A
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 2
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ #
+ # new diff:
+ # file_name -> new_file_name
+ # 1 1 BB
+ # 2 - A
+ # 2 + AA
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: file_name,
+ new_path: new_file_name,
+ old_line: old_position.new_line,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, move_line_again_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ #
+ # new diff:
+ # file_name -> new_file_name
+ # 1 + AA
+ # 1 2 BB
+ # 2 - A
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: file_name,
+ new_path: new_file_name,
+ old_line: 1,
+ new_line: 2
+ )
+ end
+ end
+
+ context "when that line was changed between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
+ let(:change_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ #
+ # new diff:
+ # file_name -> new_file_name
+ # 1 1 BB
+ # 2 - A
+ # 2 + AA
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: new_file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file is deleted in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ #
+ # new diff:
+ # 1 - BB
+ # 2 - A
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+ #
+ # new diff:
+ # 1 - BB
+ # 2 - A
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 - BB
+ # 2 - A
+ # 3 - C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was changed between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, delete_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 - A
+ # 2 - BB
+ # 3 - C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 3) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+ #
+ # new diff:
+ # 1 - BB
+ # 2 - A
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file is unchanged in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, create_second_file_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 2 B
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 2
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 2 BB
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(move_line_commit, move_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 BB
+ # 2 2 A
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: nil,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 2 BB
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when that line was deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(delete_line_commit, delete_second_file_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_second_file_line_commit) }
+ let(:old_position) { position(new_path: file_name, new_line: 3) }
+
+ # old diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+ #
+ # new diff:
+ # 1 1 BB
+ # 2 2 A
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file was changed in the old diff" do
+ context "when the file is created in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + BB
+ # 2 + A
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + BB
+ # 2 + A
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed or deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, create_file_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, create_file_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + B
+ # 3 + C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 1,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+
+ context "when the position pointed at a deleted line in the old diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, initial_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 2,
+ new_line: nil
+ )
+ end
+ end
+
+ context "when the position pointed at an unchanged line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 1, new_line: 1) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 1, new_line: 2) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + BB
+ # 2 + A
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2, new_line: 2) }
+
+ # old diff:
+ # 1 1 BB
+ # 2 2 A
+ # 3 - C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + BB
+ # 3 + C
+
+ it "returns the new position" do
+ expect_new_position(
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed or deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 3, new_line: 3) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 + A
+ # 2 + B
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 3,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+
+ context "when the file is changed in the new diff" do
+ context "when the position pointed at an added line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: old_position.old_path,
+ new_path: old_position.new_path,
+ old_line: nil,
+ new_line: old_position.new_line
+ )
+ end
+ end
+
+ context "when the file's content was changed between the old and the new diff" do
+ context "when that line was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 1 BB
+ # 2 2 A
+ # 3 - C
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: old_position.old_path,
+ new_path: old_position.new_path,
+ old_line: 1,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was moved between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 - A
+ # 2 1 BB
+ # 2 + A
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: old_position.old_path,
+ new_path: old_position.new_path,
+ old_line: 2,
+ new_line: 1
+ )
+ end
+ end
+
+ context "when that line was changed or deleted between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:change_diff_refs) { diff_refs(move_line_commit, update_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
+
+ # old diff:
+ # 1 + BB
+ # 1 2 A
+ # 2 - B
+ # 3 3 C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+
+ it "returns the position of the change" do
+ expect_change_position(
+ old_path: file_name,
+ new_path: file_name,
+ old_line: 1,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+
+ context "when the position pointed at a deleted line in the old diff" do
+ context "when the file's content was unchanged between the old and the new diff" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
+ let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+
+ it "returns the new position" do
+ expect_new_position(
+ old_path: old_position.old_path,
+ new_path: old_position.new_path,
+ old_line: old_position.old_line,
+ new_line: nil
+ )
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe "typical use scenarios" do
+ let(:second_branch_name) { "#{branch_name}-2" }
+
+ def expect_new_positions(old_attrs, new_attrs)
+ old_positions = old_attrs.map do |old_attrs|
+ position(old_attrs)
+ end
+
+ new_positions = old_positions.map do |old_position|
+ strategy.trace(old_position)
+ end
+
+ aggregate_failures do
+ new_positions.zip(new_attrs).each do |new_position, new_attrs|
+ if new_attrs&.delete(:change)
+ expect_change_position(new_attrs, new_position)
+ else
+ expect_new_position(new_attrs, new_position)
+ end
+ end
+ end
+ end
+
+ let(:create_file_commit) do
+ initial_commit
+
+ create_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ B
+ C
+ D
+ E
+ F
+ CONTENT
+ )
+ end
+
+ let(:second_create_file_commit) do
+ create_file_commit
+
+ create_branch(second_branch_name, branch_name)
+
+ update_file(
+ second_branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ Z
+ Z
+ Z
+ A
+ B
+ C
+ D
+ E
+ F
+ CONTENT
+ )
+ end
+
+ let(:update_file_commit) do
+ second_create_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ C
+ DD
+ E
+ F
+ G
+ CONTENT
+ )
+ end
+
+ let(:update_file_again_commit) do
+ update_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ BB
+ C
+ D
+ E
+ FF
+ G
+ CONTENT
+ )
+ end
+
+ describe "simple push of new commit" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 3 2 C
+ # 4 - D
+ # 3 + DD
+ # 5 4 E
+ # 6 5 F
+ # 6 + G
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 }, # C
+ { old_path: file_name, old_line: 4 }, # - D
+ { new_path: file_name, new_line: 3 }, # + DD
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 }, # E
+ { old_path: file_name, new_path: file_name, old_line: 6, new_line: 5 }, # F
+ { new_path: file_name, new_line: 6 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
+ { old_path: file_name, old_line: 2 },
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
+ { new_path: file_name, new_line: 4, change: true },
+ { new_path: file_name, old_line: 3, change: true },
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
+ { new_path: file_name, old_line: 5, change: true },
+ { new_path: file_name, new_line: 7 }
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+
+ describe "force push to overwrite last commit" do
+ let(:second_create_file_commit) do
+ create_file_commit
+
+ create_branch(second_branch_name, branch_name)
+
+ update_file(
+ second_branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ BB
+ C
+ D
+ E
+ FF
+ G
+ CONTENT
+ )
+ end
+
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, second_create_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_commit, second_create_file_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 3 2 C
+ # 4 - D
+ # 3 + DD
+ # 5 4 E
+ # 6 5 F
+ # 6 + G
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 }, # C
+ { old_path: file_name, old_line: 4 }, # - D
+ { new_path: file_name, new_line: 3 }, # + DD
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 }, # E
+ { old_path: file_name, new_path: file_name, old_line: 6, new_line: 5 }, # F
+ { new_path: file_name, new_line: 6 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
+ { old_path: file_name, old_line: 2 },
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
+ { new_path: file_name, new_line: 4, change: true },
+ { old_path: file_name, old_line: 3, change: true },
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
+ { old_path: file_name, old_line: 5, change: true },
+ { new_path: file_name, new_line: 7 }
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+
+ describe "force push to delete last commit" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, update_file_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+ #
+ # new diff:
+ # 1 1 A
+ # 2 - B
+ # 3 2 C
+ # 4 - D
+ # 3 + DD
+ # 5 4 E
+ # 6 5 F
+ # 6 + G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 2 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 6 }, # + FF
+ { new_path: file_name, new_line: 7 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
+ { old_path: file_name, old_line: 2 },
+ { old_path: file_name, old_line: 2, change: true },
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 },
+ { old_path: file_name, old_line: 4, change: true },
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 },
+ { new_path: file_name, new_line: 5, change: true },
+ { old_path: file_name, old_line: 6, change: true },
+ { new_path: file_name, new_line: 6 }
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+
+ describe "rebase on top of target branch" do
+ let(:second_update_file_commit) do
+ update_file_commit
+
+ update_file(
+ second_branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ Z
+ Z
+ Z
+ A
+ C
+ DD
+ E
+ F
+ G
+ CONTENT
+ )
+ end
+
+ let(:update_file_again_commit) do
+ second_update_file_commit
+
+ update_file(
+ branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ A
+ BB
+ C
+ D
+ E
+ FF
+ G
+ CONTENT
+ )
+ end
+
+ let(:overwrite_update_file_again_commit) do
+ update_file_again_commit
+
+ update_file(
+ second_branch_name,
+ file_name,
+ <<-CONTENT.strip_heredoc
+ Z
+ Z
+ Z
+ A
+ BB
+ C
+ D
+ E
+ FF
+ G
+ CONTENT
+ )
+ end
+
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, overwrite_update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, overwrite_update_file_again_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+ #
+ # new diff:
+ # 1 + Z
+ # 2 + Z
+ # 3 + Z
+ # 1 4 A
+ # 2 - B
+ # 5 + BB
+ # 3 6 C
+ # 4 7 D
+ # 5 8 E
+ # 6 - F
+ # 9 + FF
+ # 0 + G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 2 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 6 }, # + FF
+ { new_path: file_name, new_line: 7 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 4 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 5 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 6 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 7 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 8 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 9 }, # + FF
+ { new_path: file_name, new_line: 10 } # + G
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+
+ describe "merge of target branch" do
+ let(:merge_commit) do
+ second_create_file_commit
+
+ merge_request = create(:merge_request, source_branch: second_branch_name, target_branch: branch_name, source_project: project)
+
+ repository.merge(current_user, merge_request.diff_head_sha, merge_request, "Merge branches")
+
+ project.commit(branch_name)
+ end
+
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:new_diff_refs) { diff_refs(create_file_commit, merge_commit) }
+ let(:change_diff_refs) { diff_refs(update_file_again_commit, merge_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+ #
+ # new diff:
+ # 1 + Z
+ # 2 + Z
+ # 3 + Z
+ # 1 4 A
+ # 2 - B
+ # 5 + BB
+ # 3 6 C
+ # 4 7 D
+ # 5 8 E
+ # 6 - F
+ # 9 + FF
+ # 0 + G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 2 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 6 }, # + FF
+ { new_path: file_name, new_line: 7 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 4 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 5 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 6 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 7 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 8 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 9 }, # + FF
+ { new_path: file_name, new_line: 10 } # + G
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+
+ describe "changing target branch" do
+ let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
+ let(:new_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
+ let(:change_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
+
+ # old diff:
+ # 1 1 A
+ # 2 - B
+ # 2 + BB
+ # 3 3 C
+ # 4 4 D
+ # 5 5 E
+ # 6 - F
+ # 6 + FF
+ # 7 + G
+ #
+ # new diff:
+ # 1 1 A
+ # 2 + BB
+ # 2 3 C
+ # 3 - DD
+ # 4 + D
+ # 4 5 E
+ # 5 - F
+ # 6 + FF
+ # 7 G
+
+ it "returns the new positions" do
+ old_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
+ { old_path: file_name, old_line: 2 }, # - B
+ { new_path: file_name, new_line: 2 }, # + BB
+ { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
+ { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
+ { old_path: file_name, old_line: 6 }, # - F
+ { new_path: file_name, new_line: 6 }, # + FF
+ { new_path: file_name, new_line: 7 } # + G
+ ]
+
+ new_position_attrs = [
+ { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
+ { old_path: file_name, old_line: 2, change: true },
+ { new_path: file_name, new_line: 2 },
+ { old_path: file_name, new_path: file_name, old_line: 2, new_line: 3 },
+ { new_path: file_name, new_line: 4 },
+ { old_path: file_name, new_path: file_name, old_line: 4, new_line: 5 },
+ { old_path: file_name, old_line: 5 },
+ { new_path: file_name, new_line: 6 },
+ { new_path: file_name, new_line: 7 }
+ ]
+
+ expect_new_positions(old_position_attrs, new_position_attrs)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb
index 866550753a8..79b33d4d276 100644
--- a/spec/lib/gitlab/diff/position_tracer_spec.rb
+++ b/spec/lib/gitlab/diff/position_tracer_spec.rb
@@ -1,1896 +1,98 @@
require 'spec_helper'
describe Gitlab::Diff::PositionTracer do
- # Douwe's diary New York City, 2016-06-28
- # --------------------------------------------------------------------------
- #
- # Dear diary,
- #
- # Ideally, we would have a test for every single diff scenario that can
- # occur and that the PositionTracer should correctly trace a position
- # through, across the following variables:
- #
- # - Old diff file type: created, changed, renamed, deleted, unchanged (5)
- # - Old diff line type: added, removed, unchanged (3)
- # - New diff file type: created, changed, renamed, deleted, unchanged (5)
- # - New diff line type: added, removed, unchanged (3)
- # - Old-to-new diff line change: kept, moved, undone (3)
- #
- # This adds up to 5 * 3 * 5 * 3 * 3 = 675 different potential scenarios,
- # and 675 different tests to cover them all. In reality, it would be fewer,
- # since one cannot have a removed line in a created file diff, for example,
- # but for the sake of this diary entry, let's be pessimistic.
- #
- # Writing these tests is a manual and time consuming process, as every test
- # requires the manual construction or finding of a combination of diffs that
- # create the exact diff scenario we are looking for, and can take between
- # 1 and 10 minutes, depending on the farfetchedness of the scenario and
- # complexity of creating it.
- #
- # This means that writing tests to cover all of these scenarios would end up
- # taking between 11 and 112 hours in total, which I do not believe is the
- # best use of my time.
- #
- # A better course of action would be to think of scenarios that are likely
- # to occur, but also potentially tricky to trace correctly, and only cover
- # those, with a few more obvious scenarios thrown in to cover our bases.
- #
- # Unfortunately, I only came to the above realization once I was about
- # 1/5th of the way through the process of writing ALL THE SPECS, having
- # already wasted about 3 hours trying to be thorough.
- #
- # I did find 2 bugs while writing those though, so that's good.
- #
- # In any case, all of this means that the tests below will be extremely
- # (excessively, unjustifiably) thorough for scenarios where "the file was
- # created in the old diff" and then drop off to comparatively lackluster
- # testing of other scenarios.
- #
- # I did still try to cover most of the obvious and potentially tricky
- # scenarios, though.
+ include PositionTracerHelpers
- include RepoHelpers
-
- let(:project) { create(:project, :repository) }
- let(:current_user) { project.owner }
- let(:repository) { project.repository }
- let(:file_name) { "test-file" }
- let(:new_file_name) { "#{file_name}-new" }
- let(:second_file_name) { "#{file_name}-2" }
- let(:branch_name) { "position-tracer-test" }
-
- let(:old_diff_refs) { raise NotImplementedError }
- let(:new_diff_refs) { raise NotImplementedError }
- let(:change_diff_refs) { raise NotImplementedError }
- let(:old_position) { raise NotImplementedError }
-
- let(:position_tracer) { described_class.new(project: project, old_diff_refs: old_diff_refs, new_diff_refs: new_diff_refs) }
- subject { position_tracer.trace(old_position) }
-
- def diff_refs(base_commit, head_commit)
- Gitlab::Diff::DiffRefs.new(base_sha: base_commit.id, head_sha: head_commit.id)
- end
-
- def text_position_attrs
- [:old_line, :new_line]
- end
-
- def position(attrs = {})
- attrs.reverse_merge!(
- diff_refs: old_diff_refs
+ subject do
+ described_class.new(
+ project: project,
+ old_diff_refs: old_diff_refs,
+ new_diff_refs: new_diff_refs
)
- Gitlab::Diff::Position.new(attrs)
end
- def expect_new_position(attrs, result = subject)
- aggregate_failures("expect new position #{attrs.inspect}") do
- if attrs.nil?
- expect(result[:outdated]).to be_truthy
- else
- expect(result[:outdated]).to be_falsey
+ describe '#trace' do
+ let(:diff_refs) { double(complete?: true) }
+ let(:project) { double }
+ let(:old_diff_refs) { diff_refs }
+ let(:new_diff_refs) { diff_refs }
+ let(:position) { double(on_text?: on_text?, diff_refs: diff_refs) }
+ let(:tracer) { double }
- new_position = result[:position]
- expect(new_position).not_to be_nil
+ context 'position is on text' do
+ let(:on_text?) { true }
- expect(new_position.diff_refs).to eq(new_diff_refs)
+ it 'calls LineStrategy#trace' do
+ expect(Gitlab::Diff::PositionTracer::LineStrategy)
+ .to receive(:new)
+ .with(subject)
+ .and_return(tracer)
+ expect(tracer).to receive(:trace).with(position)
- attrs.each do |attr, value|
- if text_position_attrs.include?(attr)
- expect(new_position.formatter.send(attr)).to eq(value)
- else
- expect(new_position.send(attr)).to eq(value)
- end
- end
+ subject.trace(position)
end
end
- end
-
- def expect_change_position(attrs, result = subject)
- aggregate_failures("expect change position #{attrs.inspect}") do
- expect(result[:outdated]).to be_truthy
-
- change_position = result[:position]
- if attrs.nil? || attrs.empty?
- expect(change_position).to be_nil
- else
- expect(change_position).not_to be_nil
-
- expect(change_position.diff_refs).to eq(change_diff_refs)
-
- attrs.each do |attr, value|
- if text_position_attrs.include?(attr)
- expect(change_position.formatter.send(attr)).to eq(value)
- else
- expect(change_position.send(attr)).to eq(value)
- end
- end
- end
- end
- end
-
- def create_branch(new_name, branch_name)
- CreateBranchService.new(project, current_user).execute(new_name, branch_name)
- end
-
- def create_file(branch_name, file_name, content)
- Files::CreateService.new(
- project,
- current_user,
- start_branch: branch_name,
- branch_name: branch_name,
- commit_message: "Create file",
- file_path: file_name,
- file_content: content
- ).execute
- project.commit(branch_name)
- end
-
- def update_file(branch_name, file_name, content)
- Files::UpdateService.new(
- project,
- current_user,
- start_branch: branch_name,
- branch_name: branch_name,
- commit_message: "Update file",
- file_path: file_name,
- file_content: content
- ).execute
- project.commit(branch_name)
- end
-
- def delete_file(branch_name, file_name)
- Files::DeleteService.new(
- project,
- current_user,
- start_branch: branch_name,
- branch_name: branch_name,
- commit_message: "Delete file",
- file_path: file_name
- ).execute
- project.commit(branch_name)
- end
-
- let(:initial_commit) do
- create_branch(branch_name, "master")[:branch].name
- project.commit(branch_name)
- end
-
- describe "#trace" do
- describe "diff scenarios" do
- let(:create_file_commit) do
- initial_commit
-
- create_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- B
- C
- CONTENT
- )
- end
-
- let(:create_second_file_commit) do
- create_file_commit
-
- create_file(
- branch_name,
- second_file_name,
- <<-CONTENT.strip_heredoc
- D
- E
- CONTENT
- )
- end
-
- let(:update_line_commit) do
- create_second_file_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- BB
- C
- CONTENT
- )
- end
-
- let(:update_second_file_line_commit) do
- update_line_commit
-
- update_file(
- branch_name,
- second_file_name,
- <<-CONTENT.strip_heredoc
- D
- EE
- CONTENT
- )
- end
-
- let(:move_line_commit) do
- update_second_file_line_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- BB
- A
- C
- CONTENT
- )
- end
-
- let(:add_second_file_line_commit) do
- move_line_commit
-
- update_file(
- branch_name,
- second_file_name,
- <<-CONTENT.strip_heredoc
- D
- EE
- F
- CONTENT
- )
- end
-
- let(:move_second_file_line_commit) do
- add_second_file_line_commit
-
- update_file(
- branch_name,
- second_file_name,
- <<-CONTENT.strip_heredoc
- D
- F
- EE
- CONTENT
- )
- end
-
- let(:delete_line_commit) do
- move_second_file_line_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- BB
- A
- CONTENT
- )
- end
-
- let(:delete_second_file_line_commit) do
- delete_line_commit
-
- update_file(
- branch_name,
- second_file_name,
- <<-CONTENT.strip_heredoc
- D
- F
- CONTENT
- )
- end
-
- let(:delete_file_commit) do
- delete_second_file_line_commit
-
- delete_file(branch_name, file_name)
- end
-
- let(:rename_file_commit) do
- delete_file_commit
-
- create_file(
- branch_name,
- new_file_name,
- <<-CONTENT.strip_heredoc
- BB
- A
- CONTENT
- )
- end
-
- let(:update_line_again_commit) do
- rename_file_commit
-
- update_file(
- branch_name,
- new_file_name,
- <<-CONTENT.strip_heredoc
- BB
- AA
- CONTENT
- )
- end
-
- let(:move_line_again_commit) do
- update_line_again_commit
-
- update_file(
- branch_name,
- new_file_name,
- <<-CONTENT.strip_heredoc
- AA
- BB
- CONTENT
- )
- end
-
- let(:delete_line_again_commit) do
- move_line_again_commit
-
- update_file(
- branch_name,
- new_file_name,
- <<-CONTENT.strip_heredoc
- AA
- CONTENT
- )
- end
-
- context "when the file was created in the old diff" do
- context "when the file is created in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, create_second_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 + A
- # 2 + B
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 + BB
- # 2 + A
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:change_diff_refs) { diff_refs(update_line_commit, delete_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 3) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 + A
- # 2 + BB
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 3,
- new_line: nil
- )
- end
- end
- end
- end
- end
-
- context "when the file is changed in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 3) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 - A
- # 2 1 BB
- # 2 + A
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 - A
- # 2 1 BB
- # 2 + A
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 3) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- # 3 + C
- #
- # new diff:
- # 1 1 BB
- # 2 2 A
- # 3 - C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 3,
- new_line: nil
- )
- end
- end
- end
- end
- end
-
- context "when the file is renamed in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, rename_file_commit) }
- let(:change_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # file_name -> new_file_name
- # 1 1 BB
- # 2 2 A
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: nil,
- new_line: 2
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # file_name -> new_file_name
- # 1 1 BB
- # 2 - A
- # 2 + AA
-
- it "returns the new position" do
- expect_new_position(
- old_path: file_name,
- new_path: new_file_name,
- old_line: old_position.new_line,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, move_line_again_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # file_name -> new_file_name
- # 1 + AA
- # 1 2 BB
- # 2 - A
-
- it "returns the new position" do
- expect_new_position(
- old_path: file_name,
- new_path: new_file_name,
- old_line: 1,
- new_line: 2
- )
- end
- end
-
- context "when that line was changed between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
- let(:change_diff_refs) { diff_refs(delete_line_commit, update_line_again_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # file_name -> new_file_name
- # 1 1 BB
- # 2 - A
- # 2 + AA
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: new_file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
- end
- end
- end
-
- context "when the file is deleted in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
- let(:change_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- #
- # new diff:
- # 1 - BB
- # 2 - A
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- # 3 + C
- #
- # new diff:
- # 1 - BB
- # 2 - A
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
- let(:change_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 - BB
- # 2 - A
- # 3 - C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was changed between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, delete_file_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, delete_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 - A
- # 2 - BB
- # 3 - C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, delete_file_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, delete_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 3) }
- # old diff:
- # 1 + BB
- # 2 + A
- # 3 + C
- #
- # new diff:
- # 1 - BB
- # 2 - A
+ context 'position is not on text' do
+ let(:on_text?) { false }
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 3,
- new_line: nil
- )
- end
- end
- end
- end
- end
+ it 'calls ImageStrategy#trace' do
+ expect(Gitlab::Diff::PositionTracer::ImageStrategy)
+ .to receive(:new)
+ .with(subject)
+ .and_return(tracer)
+ expect(tracer).to receive(:trace).with(position)
- context "when the file is unchanged in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, create_second_file_commit) }
- let(:change_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 1 A
- # 2 2 B
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: nil,
- new_line: 2
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
- let(:change_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 1 A
- # 2 2 BB
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: nil,
- new_line: 1
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(move_line_commit, move_second_file_line_commit) }
- let(:change_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + BB
- # 3 + C
- #
- # new diff:
- # 1 1 BB
- # 2 2 A
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: nil,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, update_second_file_line_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 + A
- # 2 + B
- # 3 + C
- #
- # new diff:
- # 1 1 A
- # 2 2 BB
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when that line was deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(delete_line_commit, delete_second_file_line_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, delete_second_file_line_commit) }
- let(:old_position) { position(new_path: file_name, new_line: 3) }
-
- # old diff:
- # 1 + BB
- # 2 + A
- # 3 + C
- #
- # new diff:
- # 1 1 BB
- # 2 2 A
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 3,
- new_line: nil
- )
- end
- end
- end
- end
- end
- end
-
- context "when the file was changed in the old diff" do
- context "when the file is created in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 + BB
- # 2 + A
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 + BB
- # 2 + A
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed or deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, create_file_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, create_file_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 + A
- # 2 + B
- # 3 + C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 1,
- new_line: nil
- )
- end
- end
- end
- end
-
- context "when the position pointed at a deleted line in the old diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, initial_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 2,
- new_line: nil
- )
- end
- end
-
- context "when the position pointed at an unchanged line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 1, new_line: 1) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, move_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 1, new_line: 2) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 + BB
- # 2 + A
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, update_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2, new_line: 2) }
-
- # old diff:
- # 1 1 BB
- # 2 2 A
- # 3 - C
- #
- # new diff:
- # 1 + A
- # 2 + BB
- # 3 + C
-
- it "returns the new position" do
- expect_new_position(
- new_path: old_position.new_path,
- old_line: nil,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed or deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(initial_commit, delete_line_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 3, new_line: 3) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 + A
- # 2 + B
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 3,
- new_line: nil
- )
- end
- end
- end
- end
- end
-
- context "when the file is changed in the new diff" do
- context "when the position pointed at an added line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- old_path: old_position.old_path,
- new_path: old_position.new_path,
- old_line: nil,
- new_line: old_position.new_line
- )
- end
- end
-
- context "when the file's content was changed between the old and the new diff" do
- context "when that line was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(move_line_commit, delete_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 1 BB
- # 2 2 A
- # 3 - C
-
- it "returns the new position" do
- expect_new_position(
- old_path: old_position.old_path,
- new_path: old_position.new_path,
- old_line: 1,
- new_line: 1
- )
- end
- end
-
- context "when that line was moved between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(update_line_commit, move_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 - A
- # 2 1 BB
- # 2 + A
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- old_path: old_position.old_path,
- new_path: old_position.new_path,
- old_line: 2,
- new_line: 1
- )
- end
- end
-
- context "when that line was changed or deleted between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, move_line_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:change_diff_refs) { diff_refs(move_line_commit, update_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, new_line: 1) }
-
- # old diff:
- # 1 + BB
- # 1 2 A
- # 2 - B
- # 3 3 C
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
-
- it "returns the position of the change" do
- expect_change_position(
- old_path: file_name,
- new_path: file_name,
- old_line: 1,
- new_line: nil
- )
- end
- end
- end
- end
-
- context "when the position pointed at a deleted line in the old diff" do
- context "when the file's content was unchanged between the old and the new diff" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_line_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_second_file_line_commit) }
- let(:old_position) { position(old_path: file_name, new_path: file_name, old_line: 2) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
-
- it "returns the new position" do
- expect_new_position(
- old_path: old_position.old_path,
- new_path: old_position.new_path,
- old_line: old_position.old_line,
- new_line: nil
- )
- end
- end
- end
- end
+ subject.trace(position)
end
end
end
- describe "typical use scenarios" do
- let(:second_branch_name) { "#{branch_name}-2" }
-
- def expect_new_positions(old_attrs, new_attrs)
- old_positions = old_attrs.map do |old_attrs|
- position(old_attrs)
- end
+ describe 'diffs methods' do
+ let(:project) { create(:project, :repository) }
+ let(:current_user) { project.owner }
- new_positions = old_positions.map do |old_position|
- position_tracer.trace(old_position)
- end
-
- aggregate_failures do
- new_positions.zip(new_attrs).each do |new_position, new_attrs|
- if new_attrs&.delete(:change)
- expect_change_position(new_attrs, new_position)
- else
- expect_new_position(new_attrs, new_position)
- end
- end
- end
- end
-
- let(:create_file_commit) do
- initial_commit
-
- create_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- B
- C
- D
- E
- F
- CONTENT
- )
- end
-
- let(:second_create_file_commit) do
- create_file_commit
-
- create_branch(second_branch_name, branch_name)
-
- update_file(
- second_branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- Z
- Z
- Z
- A
- B
- C
- D
- E
- F
- CONTENT
+ let(:old_diff_refs) do
+ diff_refs(
+ project.commit(create_branch('new-branch', 'master')[:branch].name),
+ create_file('new-branch', 'file.md', 'content')
)
end
- let(:update_file_commit) do
- second_create_file_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- C
- DD
- E
- F
- G
- CONTENT
- )
- end
-
- let(:update_file_again_commit) do
- update_file_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- BB
- C
- D
- E
- FF
- G
- CONTENT
+ let(:new_diff_refs) do
+ diff_refs(
+ create_file('new-branch', 'file.md', 'content'),
+ update_file('new-branch', 'file.md', 'updatedcontent')
)
end
- describe "simple push of new commit" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
- let(:change_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 3 2 C
- # 4 - D
- # 3 + DD
- # 5 4 E
- # 6 5 F
- # 6 + G
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 }, # C
- { old_path: file_name, old_line: 4 }, # - D
- { new_path: file_name, new_line: 3 }, # + DD
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 }, # E
- { old_path: file_name, new_path: file_name, old_line: 6, new_line: 5 }, # F
- { new_path: file_name, new_line: 6 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
- { old_path: file_name, old_line: 2 },
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
- { new_path: file_name, new_line: 4, change: true },
- { new_path: file_name, old_line: 3, change: true },
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
- { new_path: file_name, old_line: 5, change: true },
- { new_path: file_name, new_line: 7 }
- ]
+ describe '#ac_diffs' do
+ it 'returns the diffs between the base of old and new diff' do
+ diff_refs = subject.ac_diffs.diff_refs
- expect_new_positions(old_position_attrs, new_position_attrs)
+ expect(diff_refs.base_sha).to eq(old_diff_refs.base_sha)
+ expect(diff_refs.start_sha).to eq(old_diff_refs.base_sha)
+ expect(diff_refs.head_sha).to eq(new_diff_refs.base_sha)
end
end
- describe "force push to overwrite last commit" do
- let(:second_create_file_commit) do
- create_file_commit
+ describe '#bd_diffs' do
+ it 'returns the diffs between the HEAD of old and new diff' do
+ diff_refs = subject.bd_diffs.diff_refs
- create_branch(second_branch_name, branch_name)
-
- update_file(
- second_branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- BB
- C
- D
- E
- FF
- G
- CONTENT
- )
- end
-
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, second_create_file_commit) }
- let(:change_diff_refs) { diff_refs(update_file_commit, second_create_file_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 3 2 C
- # 4 - D
- # 3 + DD
- # 5 4 E
- # 6 5 F
- # 6 + G
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 }, # C
- { old_path: file_name, old_line: 4 }, # - D
- { new_path: file_name, new_line: 3 }, # + DD
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 }, # E
- { old_path: file_name, new_path: file_name, old_line: 6, new_line: 5 }, # F
- { new_path: file_name, new_line: 6 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
- { old_path: file_name, old_line: 2 },
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 },
- { new_path: file_name, new_line: 4, change: true },
- { old_path: file_name, old_line: 3, change: true },
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 },
- { old_path: file_name, old_line: 5, change: true },
- { new_path: file_name, new_line: 7 }
- ]
-
- expect_new_positions(old_position_attrs, new_position_attrs)
+ expect(diff_refs.base_sha).to eq(old_diff_refs.head_sha)
+ expect(diff_refs.start_sha).to eq(old_diff_refs.head_sha)
+ expect(diff_refs.head_sha).to eq(new_diff_refs.head_sha)
end
end
- describe "force push to delete last commit" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
- let(:change_diff_refs) { diff_refs(update_file_again_commit, update_file_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
- #
- # new diff:
- # 1 1 A
- # 2 - B
- # 3 2 C
- # 4 - D
- # 3 + DD
- # 5 4 E
- # 6 5 F
- # 6 + G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 2 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 6 }, # + FF
- { new_path: file_name, new_line: 7 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
- { old_path: file_name, old_line: 2 },
- { old_path: file_name, old_line: 2, change: true },
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 2 },
- { old_path: file_name, old_line: 4, change: true },
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 4 },
- { new_path: file_name, new_line: 5, change: true },
- { old_path: file_name, old_line: 6, change: true },
- { new_path: file_name, new_line: 6 }
- ]
-
- expect_new_positions(old_position_attrs, new_position_attrs)
- end
- end
-
- describe "rebase on top of target branch" do
- let(:second_update_file_commit) do
- update_file_commit
-
- update_file(
- second_branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- Z
- Z
- Z
- A
- C
- DD
- E
- F
- G
- CONTENT
- )
- end
-
- let(:update_file_again_commit) do
- second_update_file_commit
-
- update_file(
- branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- A
- BB
- C
- D
- E
- FF
- G
- CONTENT
- )
- end
-
- let(:overwrite_update_file_again_commit) do
- update_file_again_commit
-
- update_file(
- second_branch_name,
- file_name,
- <<-CONTENT.strip_heredoc
- Z
- Z
- Z
- A
- BB
- C
- D
- E
- FF
- G
- CONTENT
- )
- end
-
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, overwrite_update_file_again_commit) }
- let(:change_diff_refs) { diff_refs(update_file_again_commit, overwrite_update_file_again_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
- #
- # new diff:
- # 1 + Z
- # 2 + Z
- # 3 + Z
- # 1 4 A
- # 2 - B
- # 5 + BB
- # 3 6 C
- # 4 7 D
- # 5 8 E
- # 6 - F
- # 9 + FF
- # 0 + G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 2 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 6 }, # + FF
- { new_path: file_name, new_line: 7 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 4 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 5 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 6 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 7 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 8 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 9 }, # + FF
- { new_path: file_name, new_line: 10 }, # + G
- ]
-
- expect_new_positions(old_position_attrs, new_position_attrs)
- end
- end
-
- describe "merge of target branch" do
- let(:merge_commit) do
- second_create_file_commit
-
- merge_request = create(:merge_request, source_branch: second_branch_name, target_branch: branch_name, source_project: project)
-
- repository.merge(current_user, merge_request.diff_head_sha, merge_request, "Merge branches")
-
- project.commit(branch_name)
- end
-
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
- let(:new_diff_refs) { diff_refs(create_file_commit, merge_commit) }
- let(:change_diff_refs) { diff_refs(update_file_again_commit, merge_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
- #
- # new diff:
- # 1 + Z
- # 2 + Z
- # 3 + Z
- # 1 4 A
- # 2 - B
- # 5 + BB
- # 3 6 C
- # 4 7 D
- # 5 8 E
- # 6 - F
- # 9 + FF
- # 0 + G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 2 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 6 }, # + FF
- { new_path: file_name, new_line: 7 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 4 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 5 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 6 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 7 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 8 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 9 }, # + FF
- { new_path: file_name, new_line: 10 }, # + G
- ]
-
- expect_new_positions(old_position_attrs, new_position_attrs)
- end
- end
-
- describe "changing target branch" do
- let(:old_diff_refs) { diff_refs(create_file_commit, update_file_again_commit) }
- let(:new_diff_refs) { diff_refs(update_file_commit, update_file_again_commit) }
- let(:change_diff_refs) { diff_refs(create_file_commit, update_file_commit) }
-
- # old diff:
- # 1 1 A
- # 2 - B
- # 2 + BB
- # 3 3 C
- # 4 4 D
- # 5 5 E
- # 6 - F
- # 6 + FF
- # 7 + G
- #
- # new diff:
- # 1 1 A
- # 2 + BB
- # 2 3 C
- # 3 - DD
- # 4 + D
- # 4 5 E
- # 5 - F
- # 6 + FF
- # 7 G
-
- it "returns the new positions" do
- old_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 }, # A
- { old_path: file_name, old_line: 2 }, # - B
- { new_path: file_name, new_line: 2 }, # + BB
- { old_path: file_name, new_path: file_name, old_line: 3, new_line: 3 }, # C
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 4 }, # D
- { old_path: file_name, new_path: file_name, old_line: 5, new_line: 5 }, # E
- { old_path: file_name, old_line: 6 }, # - F
- { new_path: file_name, new_line: 6 }, # + FF
- { new_path: file_name, new_line: 7 }, # + G
- ]
-
- new_position_attrs = [
- { old_path: file_name, new_path: file_name, old_line: 1, new_line: 1 },
- { old_path: file_name, old_line: 2, change: true },
- { new_path: file_name, new_line: 2 },
- { old_path: file_name, new_path: file_name, old_line: 2, new_line: 3 },
- { new_path: file_name, new_line: 4 },
- { old_path: file_name, new_path: file_name, old_line: 4, new_line: 5 },
- { old_path: file_name, old_line: 5 },
- { new_path: file_name, new_line: 6 },
- { new_path: file_name, new_line: 7 }
- ]
+ describe '#cd_diffs' do
+ it 'returns the diffs in the new diff' do
+ diff_refs = subject.cd_diffs.diff_refs
- expect_new_positions(old_position_attrs, new_position_attrs)
+ expect(diff_refs.base_sha).to eq(new_diff_refs.base_sha)
+ expect(diff_refs.start_sha).to eq(new_diff_refs.base_sha)
+ expect(diff_refs.head_sha).to eq(new_diff_refs.head_sha)
end
end
end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 25052a79916..3f0e6b34291 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -408,7 +408,7 @@ describe Gitlab::Git::Commit, :seed_helper do
context 'when oids is empty' do
it 'makes no Gitaly request' do
- expect(Gitlab::GitalyClient).not_to receive(:call)
+ expect(Gitlab::GitalyClient).not_to receive(:call).with(repository.storage, :commit_service, :list_commits_by_oid)
described_class.batch_by_oid(repository, [])
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index cceeae8afe6..a28b95e5bff 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1694,14 +1694,15 @@ describe Gitlab::Git::Repository, :seed_helper do
let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
let(:left_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
let(:right_branch) { 'test-master' }
+ let(:first_parent_ref) { 'refs/heads/test-master' }
let(:target_ref) { 'refs/merge-requests/999/merge' }
before do
- repository.create_branch(right_branch, branch_head) unless repository.branch_exists?(right_branch)
+ repository.create_branch(right_branch, branch_head) unless repository.ref_exists?(first_parent_ref)
end
def merge_to_ref
- repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message')
+ repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message', first_parent_ref)
end
it 'generates a commit in the target_ref' do
@@ -1716,7 +1717,7 @@ describe Gitlab::Git::Repository, :seed_helper do
end
it 'does not change the right branch HEAD' do
- expect { merge_to_ref }.not_to change { repository.find_branch(right_branch).target }
+ expect { merge_to_ref }.not_to change { repository.commit(first_parent_ref).sha }
end
end
diff --git a/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
new file mode 100644
index 00000000000..f957ed00945
--- /dev/null
+++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'json'
+require 'tempfile'
+
+describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:feature_flag_name) { 'feature-flag-name' }
+ let(:feature_flag) { Feature.get(feature_flag_name) }
+ let(:temp_gitaly_metadata_file) { create_temporary_gitaly_metadata_file }
+
+ before(:all) do
+ create_gitaly_metadata_file
+ end
+
+ subject(:wrapper) do
+ klazz = Class.new { include Gitlab::Git::RuggedImpl::UseRugged }
+ klazz.new
+ end
+
+ before do
+ Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {})
+ end
+
+ context 'when feature flag is not persisted' do
+ before do
+ allow(Feature).to receive(:persisted?).with(feature_flag).and_return(false)
+ end
+
+ it 'returns true when gitaly matches disk' do
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be true
+ end
+
+ it 'returns false when disk access fails' do
+ allow(Gitlab::GitalyClient).to receive(:storage_metadata_file_path).and_return("/fake/path/doesnt/exist")
+
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be false
+ end
+
+ it "returns false when gitaly doesn't match disk" do
+ allow(Gitlab::GitalyClient).to receive(:storage_metadata_file_path).and_return(temp_gitaly_metadata_file)
+
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be_falsey
+
+ File.delete(temp_gitaly_metadata_file)
+ end
+
+ it "doesn't lead to a second rpc call because gitaly client should use the cached value" do
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be true
+
+ expect(Gitlab::GitalyClient).not_to receive(:filesystem_id)
+
+ subject.use_rugged?(repository, feature_flag_name)
+ end
+ end
+
+ context 'when feature flag is persisted' do
+ before do
+ allow(Feature).to receive(:persisted?).with(feature_flag).and_return(true)
+ end
+
+ it 'returns false when the feature flag is off' do
+ allow(feature_flag).to receive(:enabled?).and_return(false)
+
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be_falsey
+ end
+
+ it "returns true when feature flag is on" do
+ allow(feature_flag).to receive(:enabled?).and_return(true)
+ allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(false)
+
+ expect(subject.use_rugged?(repository, feature_flag_name)).to be true
+ end
+ end
+
+ def create_temporary_gitaly_metadata_file
+ tmp = Tempfile.new('.gitaly-metadata')
+ gitaly_metadata = {
+ "gitaly_filesystem_id" => "some-value"
+ }
+ tmp.write(gitaly_metadata.to_json)
+ tmp.flush
+ tmp.close
+ tmp.path
+ end
+
+ def create_gitaly_metadata_file
+ File.open(File.join(SEED_STORAGE_PATH, '.gitaly-metadata'), 'w+') do |f|
+ gitaly_metadata = {
+ "gitaly_filesystem_id" => SecureRandom.uuid
+ }
+ f.write(gitaly_metadata.to_json)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 18663a72fcd..f38b8d31237 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -79,13 +79,13 @@ describe Gitlab::GitalyClient::OperationService do
end
describe '#user_merge_to_ref' do
- let(:branch) { 'my-branch' }
+ let(:first_parent_ref) { 'refs/heads/my-branch' }
let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
let(:ref) { 'refs/merge-requests/x/merge' }
let(:message) { 'validación' }
let(:response) { Gitaly::UserMergeToRefResponse.new(commit_id: 'new-commit-id') }
- subject { client.user_merge_to_ref(user, source_sha, branch, ref, message) }
+ subject { client.user_merge_to_ref(user, source_sha, nil, ref, message, first_parent_ref) }
it 'sends a user_merge_to_ref message' do
expect_any_instance_of(Gitaly::OperationService::Stub)
diff --git a/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb b/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb
new file mode 100644
index 00000000000..d93ce464a92
--- /dev/null
+++ b/spec/lib/gitlab/graphql/calls_gitaly/instrumentation_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::Graphql::CallsGitaly::Instrumentation do
+ subject { described_class.new }
+
+ describe '#calls_gitaly_check' do
+ let(:gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true) }
+ let(:no_gitaly_field) { Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: false) }
+
+ context 'if there are no Gitaly calls' do
+ it 'does not raise an error if calls_gitaly is false' do
+ expect { subject.send(:calls_gitaly_check, no_gitaly_field, 0) }.not_to raise_error
+ end
+ end
+
+ context 'if there is at least 1 Gitaly call' do
+ it 'raises an error if calls_gitaly: is false or not defined' do
+ expect { subject.send(:calls_gitaly_check, no_gitaly_field, 1) }.to raise_error(/specify a constant complexity or add `calls_gitaly: true`/)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index aaf8c9fa2a0..4d93b70e6e3 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -8,12 +8,19 @@ describe Gitlab::Metrics::Samplers::RubySampler do
allow(Gitlab::Metrics::NullMetric).to receive(:instance).and_return(null_metric)
end
+ describe '#initialize' do
+ it 'sets process_start_time_seconds' do
+ Timecop.freeze do
+ expect(sampler.metrics[:process_start_time_seconds].get).to eq(Time.now.to_i)
+ end
+ end
+ end
+
describe '#sample' do
it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:cpu_time)
expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
expect(Gitlab::Metrics::System).to receive(:memory_usage)
- expect(Gitlab::Metrics::System).to receive(:process_start_time)
expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors)
expect(sampler).to receive(:sample_gc)
@@ -44,13 +51,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do
sampler.sample
end
- it 'adds a metric containing the process start time' do
- expect(Gitlab::Metrics::System).to receive(:process_start_time).and_return(12345)
- expect(sampler.metrics[:process_start_time_seconds]).to receive(:set).with({}, 12345)
-
- sampler.sample
- end
-
it 'adds a metric containing the process max file descriptors' do
expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors).and_return(1024)
expect(sampler.metrics[:process_max_fds]).to receive(:set).with({}, 1024)
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index da87df15746..3b434a02f63 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -19,12 +19,6 @@ describe Gitlab::Metrics::System do
expect(described_class.max_open_file_descriptors).to be > 0
end
end
-
- describe '.process_start_time' do
- it 'returns the process start time' do
- expect(described_class.process_start_time).to be > 0
- end
- end
else
describe '.memory_usage' do
it 'returns 0.0' do
@@ -43,12 +37,6 @@ describe Gitlab::Metrics::System do
expect(described_class.max_open_file_descriptors).to eq(0)
end
end
-
- describe 'process_start_time' do
- it 'returns 0' do
- expect(described_class.process_start_time).to eq(0)
- end
- end
end
describe '.cpu_time' do
diff --git a/spec/lib/gitlab/namespaced_session_store_spec.rb b/spec/lib/gitlab/namespaced_session_store_spec.rb
index c0af2ede32a..e177c44ad67 100644
--- a/spec/lib/gitlab/namespaced_session_store_spec.rb
+++ b/spec/lib/gitlab/namespaced_session_store_spec.rb
@@ -4,19 +4,33 @@ require 'spec_helper'
describe Gitlab::NamespacedSessionStore do
let(:key) { :some_key }
- subject { described_class.new(key) }
- it 'stores data under the specified key' do
- Gitlab::Session.with_session({}) do
- subject[:new_data] = 123
+ context 'current session' do
+ subject { described_class.new(key) }
- expect(Thread.current[:session_storage][key]).to eq(new_data: 123)
+ it 'stores data under the specified key' do
+ Gitlab::Session.with_session({}) do
+ subject[:new_data] = 123
+
+ expect(Thread.current[:session_storage][key]).to eq(new_data: 123)
+ end
+ end
+
+ it 'retrieves data from the given key' do
+ Thread.current[:session_storage] = { key => { existing_data: 123 } }
+
+ expect(subject[:existing_data]).to eq 123
end
end
- it 'retrieves data from the given key' do
- Thread.current[:session_storage] = { key => { existing_data: 123 } }
+ context 'passed in session' do
+ let(:data) { { 'data' => 42 } }
+ let(:session) { { 'some_key' => data } }
+
+ subject { described_class.new(key, session.with_indifferent_access) }
- expect(subject[:existing_data]).to eq 123
+ it 'retrieves data from the given key' do
+ expect(subject['data']).to eq 42
+ end
end
end
diff --git a/spec/lib/gitlab/performance_bar_spec.rb b/spec/lib/gitlab/performance_bar_spec.rb
index f480376acb4..ee3c571c9c0 100644
--- a/spec/lib/gitlab/performance_bar_spec.rb
+++ b/spec/lib/gitlab/performance_bar_spec.rb
@@ -3,17 +3,42 @@ require 'spec_helper'
describe Gitlab::PerformanceBar do
shared_examples 'allowed user IDs are cached' do
before do
- # Warm the Redis cache
+ # Warm the caches
described_class.enabled?(user)
end
it 'caches the allowed user IDs in cache', :use_clean_rails_memory_store_caching do
expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).and_call_original
+ expect(described_class.l2_cache_backend).not_to receive(:fetch)
expect(described_class.enabled?(user)).to be_truthy
end.not_to exceed_query_limit(0)
end
+
+ it 'caches the allowed user IDs in L1 cache for 1 minute', :use_clean_rails_memory_store_caching do
+ Timecop.travel 2.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).and_call_original
+ expect(described_class.enabled?(user)).to be_truthy
+ end.not_to exceed_query_limit(0)
+ end
+ end
+
+ it 'caches the allowed user IDs in L2 cache for 5 minutes', :use_clean_rails_memory_store_caching do
+ Timecop.travel 6.minutes do
+ expect do
+ expect(described_class.l1_cache_backend).to receive(:fetch).and_call_original
+ expect(described_class.l2_cache_backend).to receive(:fetch).and_call_original
+ expect(described_class.enabled?(user)).to be_truthy
+ end.not_to exceed_query_limit(2)
+ end
+ end
end
+ it { expect(described_class.l1_cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend) }
+ it { expect(described_class.l2_cache_backend).to eq(Rails.cache) }
+
describe '.enabled?' do
let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index 5b5052de372..98838712eae 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -10,6 +10,12 @@ describe Gitlab::SQL::Pattern do
it 'returns exact matching pattern' do
expect(to_pattern).to eq('12')
end
+
+ context 'and ignore_minimum_char_limit is true' do
+ it 'returns partial matching pattern' do
+ expect(User.to_pattern(query, use_minimum_char_limit: false)).to eq('%12%')
+ end
+ end
end
context 'when a query with a escape character is shorter than 3 chars' do
@@ -18,6 +24,12 @@ describe Gitlab::SQL::Pattern do
it 'returns sanitized exact matching pattern' do
expect(to_pattern).to eq('\_2')
end
+
+ context 'and ignore_minimum_char_limit is true' do
+ it 'returns sanitized partial matching pattern' do
+ expect(User.to_pattern(query, use_minimum_char_limit: false)).to eq('%\_2%')
+ end
+ end
end
context 'when a query is equal to 3 chars' do
diff --git a/spec/lib/gitlab/user_extractor_spec.rb b/spec/lib/gitlab/user_extractor_spec.rb
deleted file mode 100644
index b86ec5445b8..00000000000
--- a/spec/lib/gitlab/user_extractor_spec.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::UserExtractor do
- let(:text) do
- <<~TXT
- This is a long texth that mentions some users.
- @user-1, @user-2 and user@gitlab.org take a walk in the park.
- There they meet @user-4 that was out with other-user@gitlab.org.
- @user-1 thought it was late, so went home straight away
- TXT
- end
- subject(:extractor) { described_class.new(text) }
-
- describe '#users' do
- it 'returns an empty relation when nil was passed' do
- extractor = described_class.new(nil)
-
- expect(extractor.users).to be_empty
- expect(extractor.users).to be_a(ActiveRecord::Relation)
- end
-
- it 'returns the user case insensitive for usernames' do
- user = create(:user, username: "USER-4")
-
- expect(extractor.users).to include(user)
- end
-
- it 'returns users by primary email' do
- user = create(:user, email: 'user@gitlab.org')
-
- expect(extractor.users).to include(user)
- end
-
- it 'returns users by secondary email' do
- user = create(:email, email: 'other-user@gitlab.org').user
-
- expect(extractor.users).to include(user)
- end
-
- context 'input as array of strings' do
- it 'is treated as one string' do
- extractor = described_class.new(text.lines)
-
- user_1 = create(:user, username: "USER-1")
- user_4 = create(:user, username: "USER-4")
- user_email = create(:user, email: 'user@gitlab.org')
-
- expect(extractor.users).to contain_exactly(user_1, user_4, user_email)
- end
- end
- end
-
- describe '#matches' do
- it 'includes all mentioned email adresses' do
- expect(extractor.matches[:emails]).to contain_exactly('user@gitlab.org', 'other-user@gitlab.org')
- end
-
- it 'includes all mentioned usernames' do
- expect(extractor.matches[:usernames]).to contain_exactly('user-1', 'user-2', 'user-4')
- end
-
- context 'input has no matching e-mail or usernames' do
- it 'returns an empty list of users' do
- extractor = described_class.new('My test')
-
- expect(extractor.users).to be_empty
- end
- end
- end
-
- describe '#references' do
- it 'includes all user-references once' do
- expect(extractor.references).to contain_exactly('user-1', 'user-2', 'user@gitlab.org', 'user-4', 'other-user@gitlab.org')
- end
- end
-end
diff --git a/spec/lib/peek/views/redis_detailed_spec.rb b/spec/lib/peek/views/redis_detailed_spec.rb
new file mode 100644
index 00000000000..da13b6df53b
--- /dev/null
+++ b/spec/lib/peek/views/redis_detailed_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Peek::Views::RedisDetailed do
+ let(:redis_detailed_class) do
+ Class.new do
+ include Peek::Views::RedisDetailed
+ end
+ end
+
+ subject { redis_detailed_class.new }
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:cmd, :expected) do
+ [:auth, 'test'] | 'auth <redacted>'
+ [:set, 'key', 'value'] | 'set key <redacted>'
+ [:set, 'bad'] | 'set bad'
+ [:hmset, 'key1', 'value1', 'key2', 'value2'] | 'hmset key1 <redacted>'
+ [:get, 'key'] | 'get key'
+ end
+
+ with_them do
+ it 'scrubs Redis commands', :request_store do
+ subject.detail_store << { cmd: cmd, duration: 1.second }
+
+ expect(subject.details.count).to eq(1)
+ expect(subject.details.first)
+ .to eq({
+ cmd: expected,
+ duration: 1000
+ })
+ end
+ end
+end
diff --git a/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb b/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb
index 34f4a36d63d..65a918d5440 100644
--- a/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb
+++ b/spec/migrations/backfill_store_project_full_path_in_repo_spec.rb
@@ -13,7 +13,7 @@ describe BackfillStoreProjectFullPathInRepo, :migration do
subject(:migration) { described_class.new }
around do |example|
- Sidekiq::Testing.inline! do
+ perform_enqueued_jobs do
example.run
end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 55cea48b641..e24bbc39761 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2998,4 +2998,28 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe '#error_messages' do
+ subject { pipeline.error_messages }
+
+ before do
+ pipeline.valid?
+ end
+
+ context 'when pipeline has errors' do
+ let(:pipeline) { build(:ci_pipeline, sha: nil, ref: nil) }
+
+ it 'returns the full error messages' do
+ is_expected.to eq("Sha can't be blank and Ref can't be blank")
+ end
+ end
+
+ context 'when pipeline does not have errors' do
+ let(:pipeline) { build(:ci_pipeline) }
+
+ it 'returns empty string' do
+ is_expected.to be_empty
+ end
+ end
+ end
end
diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb
new file mode 100644
index 00000000000..0470ebe17ea
--- /dev/null
+++ b/spec/models/clusters/clusters_hierarchy_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::ClustersHierarchy do
+ describe '#base_and_ancestors' do
+ def base_and_ancestors(clusterable)
+ described_class.new(clusterable).base_and_ancestors
+ end
+
+ context 'project in nested group with clusters at every level' do
+ let!(:cluster) { create(:cluster, :project, projects: [project]) }
+ let!(:child) { create(:cluster, :group, groups: [child_group]) }
+ let!(:parent) { create(:cluster, :group, groups: [parent_group]) }
+ let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) }
+
+ let(:ancestor_group) { create(:group) }
+ let(:parent_group) { create(:group, parent: ancestor_group) }
+ let(:child_group) { create(:group, parent: parent_group) }
+ let(:project) { create(:project, group: child_group) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(project)).to eq([cluster, child, parent, ancestor])
+ end
+
+ it 'returns clusters for child_group' do
+ expect(base_and_ancestors(child_group)).to eq([child, parent, ancestor])
+ end
+
+ it 'returns clusters for parent_group' do
+ expect(base_and_ancestors(parent_group)).to eq([parent, ancestor])
+ end
+
+ it 'returns clusters for ancestor_group' do
+ expect(base_and_ancestors(ancestor_group)).to eq([ancestor])
+ end
+ end
+
+ context 'project in a namespace' do
+ let!(:cluster) { create(:cluster, :project) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(cluster.project)).to eq([cluster])
+ end
+ end
+
+ context 'project in nested group with clusters at some levels' do
+ let!(:child) { create(:cluster, :group, groups: [child_group]) }
+ let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) }
+
+ let(:ancestor_group) { create(:group) }
+ let(:parent_group) { create(:group, parent: ancestor_group) }
+ let(:child_group) { create(:group, parent: parent_group) }
+ let(:project) { create(:project, group: child_group) }
+
+ it 'returns clusters for project' do
+ expect(base_and_ancestors(project)).to eq([child, ancestor])
+ end
+
+ it 'returns clusters for child_group' do
+ expect(base_and_ancestors(child_group)).to eq([child, ancestor])
+ end
+
+ it 'returns clusters for parent_group' do
+ expect(base_and_ancestors(parent_group)).to eq([ancestor])
+ end
+
+ it 'returns clusters for ancestor_group' do
+ expect(base_and_ancestors(ancestor_group)).to eq([ancestor])
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index 2378f400540..e2fc8a5d127 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
describe DeploymentPlatform do
let(:project) { create(:project) }
- describe '#deployment_platform' do
+ shared_examples '#deployment_platform' do
subject { project.deployment_platform }
context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service' do
@@ -84,4 +84,20 @@ describe DeploymentPlatform do
end
end
end
+
+ context 'legacy implementation' do
+ before do
+ stub_feature_flags(clusters_cte: false)
+ end
+
+ include_examples '#deployment_platform'
+ end
+
+ context 'CTE implementation' do
+ before do
+ stub_feature_flags(clusters_cte: true)
+ end
+
+ include_examples '#deployment_platform'
+ end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 64f02978d79..68224a56515 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -223,6 +223,16 @@ describe Issuable do
expect(issuable_class.full_search(searchable_issue2.description.downcase)).to eq([searchable_issue2])
end
+ it 'returns issues with a fuzzy matching description for a query shorter than 3 chars if told to do so' do
+ search = searchable_issue2.description.downcase.scan(/\w+/).sample[-1]
+
+ expect(issuable_class.full_search(search, use_minimum_char_limit: false)).to include(searchable_issue2)
+ end
+
+ it 'returns issues with a fuzzy matching title for a query shorter than 3 chars if told to do so' do
+ expect(issuable_class.full_search('i', use_minimum_char_limit: false)).to include(searchable_issue)
+ end
+
context 'when matching columns is "title"' do
it 'returns issues with a matching title' do
expect(issuable_class.full_search(searchable_issue.title, matched_columns: 'title'))
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 7faa196623f..3d026932f59 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -47,30 +47,12 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
subject(:go!) { instance.result }
- context 'when cache is empty' do
- it { is_expected.to be_nil }
-
- it 'enqueues a background worker to bootstrap the cache' do
- expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
-
- go!
- end
-
- it 'updates the cache lifespan' do
- expect(reactive_cache_alive?(instance)).to be_falsy
-
- go!
-
- expect(reactive_cache_alive?(instance)).to be_truthy
- end
- end
-
- context 'when the cache is full' do
+ shared_examples 'a cacheable value' do |cached_value|
before do
- stub_reactive_cache(instance, 4)
+ stub_reactive_cache(instance, cached_value)
end
- it { is_expected.to eq(4) }
+ it { is_expected.to eq(cached_value) }
it 'does not enqueue a background worker' do
expect(ReactiveCachingWorker).not_to receive(:perform_async)
@@ -90,9 +72,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
it { is_expected.to be_nil }
- end
- context 'when cache was invalidated' do
it 'refreshes cache' do
expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
@@ -101,12 +81,34 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
end
- context 'when cache contains non-nil but blank value' do
- before do
- stub_reactive_cache(instance, false)
+ context 'when cache is empty' do
+ it { is_expected.to be_nil }
+
+ it 'enqueues a background worker to bootstrap the cache' do
+ expect(ReactiveCachingWorker).to receive(:perform_async).with(CacheTest, 666)
+
+ go!
end
- it { is_expected.to eq(false) }
+ it 'updates the cache lifespan' do
+ expect(reactive_cache_alive?(instance)).to be_falsy
+
+ go!
+
+ expect(reactive_cache_alive?(instance)).to be_truthy
+ end
+ end
+
+ context 'when the cache is full' do
+ it_behaves_like 'a cacheable value', 4
+ end
+
+ context 'when the cache contains non-nil but blank value' do
+ it_behaves_like 'a cacheable value', false
+ end
+
+ context 'when the cache contains nil value' do
+ it_behaves_like 'a cacheable value', nil
end
end
@@ -206,8 +208,9 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
expect(read_reactive_cache(instance)).to eq("preexisting")
end
- it 'enqueues a repeat worker' do
- expect_reactive_cache_update_queued(instance)
+ it 'does not enqueue a repeat worker' do
+ expect(ReactiveCachingWorker)
+ .not_to receive(:perform_in)
expect { go! }.to raise_error("foo")
end
diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb
new file mode 100644
index 00000000000..0aadb1f3a5e
--- /dev/null
+++ b/spec/models/deployment_metrics_spec.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DeploymentMetrics do
+ describe '#has_metrics?' do
+ subject { described_class.new(deployment.project, deployment).has_metrics? }
+
+ context 'when deployment is failed' do
+ let(:deployment) { create(:deployment, :failed) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when deployment is success' do
+ let(:deployment) { create(:deployment, :success) }
+
+ context 'without a monitoring service' do
+ it { is_expected.to be_falsy }
+ end
+
+ context 'with a Prometheus Service' do
+ let(:prometheus_service) { instance_double(PrometheusService, can_query?: true) }
+
+ before do
+ allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with a Prometheus Service that cannot query' do
+ let(:prometheus_service) { instance_double(PrometheusService, can_query?: false) }
+
+ before do
+ allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
+ end
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'with a cluster Prometheus' do
+ let(:deployment) { create(:deployment, :success, :on_cluster) }
+ let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: deployment.cluster) }
+
+ before do
+ expect(deployment.cluster.application_prometheus).to receive(:can_query?).and_return(true)
+ end
+
+ 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
+
+ describe '#metrics' do
+ let(:deployment) { create(:deployment, :success) }
+ let(:prometheus_adapter) { instance_double(PrometheusService, can_query?: true) }
+ let(:deployment_metrics) { described_class.new(deployment.project, deployment) }
+
+ subject { deployment_metrics.metrics }
+
+ context 'metrics are disabled' do
+ it { is_expected.to eq({}) }
+ end
+
+ context 'metrics are enabled' do
+ let(:simple_metrics) do
+ {
+ success: true,
+ metrics: {},
+ last_update: 42
+ }
+ end
+
+ before do
+ allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+ expect(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
+ end
+
+ it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) }
+ end
+ end
+
+ describe '#additional_metrics' do
+ let(:project) { create(:project, :repository) }
+ let(:deployment) { create(:deployment, :succeed, project: project) }
+ let(:deployment_metrics) { described_class.new(deployment.project, deployment) }
+
+ subject { deployment_metrics.additional_metrics }
+
+ context 'metrics are disabled' do
+ it { is_expected.to eq({}) }
+ end
+
+ context 'metrics are enabled' do
+ let(:simple_metrics) do
+ {
+ success: true,
+ metrics: {},
+ last_update: 42
+ }
+ end
+
+ let(:prometheus_adapter) { instance_double('prometheus_adapter', can_query?: true) }
+
+ before do
+ allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+ expect(prometheus_adapter).to receive(:query).with(:additional_metrics_deployment, deployment).and_return(simple_metrics)
+ end
+
+ it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) }
+ end
+ end
+end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 8d0eb0f4a06..d4e631f109b 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -295,125 +295,6 @@ describe Deployment do
end
end
- describe '#has_metrics?' do
- subject { deployment.has_metrics? }
-
- context 'when deployment is failed' do
- let(:deployment) { create(:deployment, :failed) }
-
- it { is_expected.to be_falsy }
- end
-
- context 'when deployment is success' do
- let(:deployment) { create(:deployment, :success) }
-
- context 'without a monitoring service' do
- it { is_expected.to be_falsy }
- end
-
- context 'with a Prometheus Service' do
- let(:prometheus_service) { double(:prometheus_service, can_query?: true) }
-
- before do
- allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'with a Prometheus Service that cannot query' do
- let(:prometheus_service) { double(:prometheus_service, can_query?: false) }
-
- before do
- allow(deployment.project).to receive(:find_or_initialize_service).with('prometheus').and_return prometheus_service
- end
-
- it { is_expected.to be_falsy }
- end
-
- context 'with a cluster Prometheus' do
- let(:deployment) { create(:deployment, :success, :on_cluster) }
- let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: deployment.cluster) }
-
- before do
- expect(deployment.cluster.application_prometheus).to receive(:can_query?).and_return(true)
- end
-
- 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
-
- describe '#metrics' do
- let(:deployment) { create(:deployment, :success) }
- let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
-
- subject { deployment.metrics }
-
- context 'metrics are disabled' do
- it { is_expected.to eq({}) }
- end
-
- context 'metrics are enabled' do
- let(:simple_metrics) do
- {
- success: true,
- metrics: {},
- last_update: 42
- }
- end
-
- before do
- allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter)
- allow(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
- end
-
- it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) }
- end
- end
-
- describe '#additional_metrics' do
- let(:project) { create(:project, :repository) }
- let(:deployment) { create(:deployment, :succeed, project: project) }
-
- subject { deployment.additional_metrics }
-
- context 'metrics are disabled' do
- it { is_expected.to eq({}) }
- end
-
- context 'metrics are enabled' do
- let(:simple_metrics) do
- {
- success: true,
- metrics: {},
- last_update: 42
- }
- end
-
- let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
-
- before do
- allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter)
- allow(prometheus_adapter).to receive(:query).with(:additional_metrics_deployment, deployment).and_return(simple_metrics)
- end
-
- it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) }
- end
- end
-
describe '#stop_action' do
let(:build) { create(:ci_build) }
@@ -441,38 +322,4 @@ describe Deployment do
end
end
end
-
- describe '#deployment_platform_cluster' do
- let(:deployment) { create(:deployment) }
- let(:project) { deployment.project }
- let(:environment) { deployment.environment }
-
- subject { deployment.deployment_platform_cluster }
-
- before do
- expect(project).to receive(:deployment_platform)
- .with(environment: environment.name).and_call_original
- end
-
- context 'project has no deployment platform' do
- before do
- expect(project.clusters).to be_empty
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'project uses the kubernetes service for deployments' do
- let!(:service) { create(:kubernetes_service, project: project) }
-
- it { is_expected.to be_nil }
- end
-
- context 'project has a deployment platform' do
- let!(:cluster) { create(:cluster, projects: [project]) }
- let!(:platform) { create(:cluster_platform_kubernetes, cluster: cluster) }
-
- it { is_expected.to eq cluster }
- end
- end
end
diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb
index c503c35305f..e2836420df9 100644
--- a/spec/models/environment_status_spec.rb
+++ b/spec/models/environment_status_spec.rb
@@ -11,11 +11,10 @@ describe EnvironmentStatus do
let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) }
let(:sha) { deployment.sha }
- subject(:environment_status) { described_class.new(environment, merge_request, sha) }
+ subject(:environment_status) { described_class.new(project, environment, merge_request, sha) }
it { is_expected.to delegate_method(:id).to(:environment) }
it { is_expected.to delegate_method(:name).to(:environment) }
- it { is_expected.to delegate_method(:project).to(:environment) }
it { is_expected.to delegate_method(:deployed_at).to(:deployment) }
it { is_expected.to delegate_method(:status).to(:deployment) }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index a2547755510..9b0c232f370 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -7,6 +7,8 @@ describe MergeRequest do
include ProjectForksHelper
include ReactiveCachingHelpers
+ using RSpec::Parameterized::TableSyntax
+
subject { create(:merge_request) }
describe 'associations' do
@@ -1996,6 +1998,47 @@ describe MergeRequest do
end
end
+ describe '#rebase_async' do
+ let(:merge_request) { create(:merge_request) }
+ let(:user_id) { double(:user_id) }
+ let(:rebase_jid) { 'rebase-jid' }
+
+ subject(:execute) { merge_request.rebase_async(user_id) }
+
+ it 'atomically enqueues a RebaseWorker job and updates rebase_jid' do
+ expect(RebaseWorker)
+ .to receive(:perform_async)
+ .with(merge_request.id, user_id)
+ .and_return(rebase_jid)
+
+ expect(merge_request).to receive(:lock!).and_call_original
+
+ execute
+
+ expect(merge_request.rebase_jid).to eq(rebase_jid)
+ end
+
+ it 'refuses to enqueue a job if a rebase is in progress' do
+ merge_request.update_column(:rebase_jid, rebase_jid)
+
+ expect(RebaseWorker).not_to receive(:perform_async)
+ expect(Gitlab::SidekiqStatus)
+ .to receive(:running?)
+ .with(rebase_jid)
+ .and_return(true)
+
+ expect { execute }.to raise_error(ActiveRecord::StaleObjectError)
+ end
+
+ it 'refuses to enqueue a job if the MR is not open' do
+ merge_request.update_column(:state, 'foo')
+
+ expect(RebaseWorker).not_to receive(:perform_async)
+
+ expect { execute }.to raise_error(ActiveRecord::StaleObjectError)
+ end
+ end
+
describe '#mergeable?' do
let(:project) { create(:project) }
@@ -2946,40 +2989,64 @@ describe MergeRequest do
end
describe '#rebase_in_progress?' do
- shared_examples 'checking whether a rebase is in progress' do
- let(:repo_path) do
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- subject.source_project.repository.path
- end
- end
- let(:rebase_path) { File.join(repo_path, "gitlab-worktree", "rebase-#{subject.id}") }
+ where(:rebase_jid, :jid_valid, :result) do
+ 'foo' | true | true
+ 'foo' | false | false
+ '' | true | false
+ nil | true | false
+ end
- before do
- system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{rebase_path} master))
+ with_them do
+ let(:merge_request) { create(:merge_request) }
+
+ subject { merge_request.rebase_in_progress? }
+
+ it do
+ # Stub out the legacy gitaly implementation
+ allow(merge_request).to receive(:gitaly_rebase_in_progress?) { false }
+
+ allow(Gitlab::SidekiqStatus).to receive(:running?).with(rebase_jid) { jid_valid }
+
+ merge_request.rebase_jid = rebase_jid
+
+ is_expected.to eq(result)
end
+ end
+ end
- it 'returns true when there is a current rebase directory' do
- expect(subject.rebase_in_progress?).to be_truthy
+ describe '#gitaly_rebase_in_progress?' do
+ let(:repo_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ subject.source_project.repository.path
end
+ end
+ let(:rebase_path) { File.join(repo_path, "gitlab-worktree", "rebase-#{subject.id}") }
- it 'returns false when there is no rebase directory' do
- FileUtils.rm_rf(rebase_path)
+ before do
+ system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{rebase_path} master))
+ end
- expect(subject.rebase_in_progress?).to be_falsey
- end
+ it 'returns true when there is a current rebase directory' do
+ expect(subject.rebase_in_progress?).to be_truthy
+ end
- it 'returns false when the rebase directory has expired' do
- time = 20.minutes.ago.to_time
- File.utime(time, time, rebase_path)
+ it 'returns false when there is no rebase directory' do
+ FileUtils.rm_rf(rebase_path)
- expect(subject.rebase_in_progress?).to be_falsey
- end
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
- it 'returns false when the source project has been removed' do
- allow(subject).to receive(:source_project).and_return(nil)
+ it 'returns false when the rebase directory has expired' do
+ time = 20.minutes.ago.to_time
+ File.utime(time, time, rebase_path)
- expect(subject.rebase_in_progress?).to be_falsey
- end
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the source project has been removed' do
+ allow(subject).to receive(:source_project).and_return(nil)
+
+ expect(subject.rebase_in_progress?).to be_falsey
end
end
@@ -3153,4 +3220,34 @@ describe MergeRequest do
it { is_expected.to be_truthy }
end
end
+
+ describe '#cleanup_refs' do
+ subject { merge_request.cleanup_refs(only: only) }
+
+ let(:merge_request) { build(:merge_request) }
+
+ context 'when removing all refs' do
+ let(:only) { :all }
+
+ it 'deletes all refs from the target project' do
+ expect(merge_request.target_project.repository)
+ .to receive(:delete_refs)
+ .with(merge_request.ref_path, merge_request.merge_ref_path, merge_request.train_ref_path)
+
+ subject
+ end
+ end
+
+ context 'when removing only train ref' do
+ let(:only) { :train }
+
+ it 'deletes train ref from the target project' do
+ expect(merge_request.target_project.repository)
+ .to receive(:delete_refs)
+ .with(merge_request.train_ref_path)
+
+ subject
+ end
+ end
+ end
end
diff --git a/spec/models/namespace/aggregation_schedule_spec.rb b/spec/models/namespace/aggregation_schedule_spec.rb
index 8ed0248e1b2..0f1283717e0 100644
--- a/spec/models/namespace/aggregation_schedule_spec.rb
+++ b/spec/models/namespace/aggregation_schedule_spec.rb
@@ -53,10 +53,39 @@ RSpec.describe Namespace::AggregationSchedule, :clean_gitlab_redis_shared_state,
expect(Namespaces::RootStatisticsWorker)
.to receive(:perform_in).once
- .with(described_class::DEFAULT_LEASE_TIMEOUT, aggregation_schedule.namespace_id )
+ .with(described_class::DEFAULT_LEASE_TIMEOUT, aggregation_schedule.namespace_id)
aggregation_schedule.save!
end
+
+ it 'does not release the lease' do
+ stub_exclusive_lease(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+
+ aggregation_schedule.save!
+
+ exclusive_lease = aggregation_schedule.exclusive_lease
+ expect(exclusive_lease.exists?).to be_truthy
+ end
+
+ it 'only executes the workers once' do
+ # Avoid automatic deletion of Namespace::AggregationSchedule
+ # for testing purposes.
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_async).once
+ .and_return(nil)
+
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_in).once
+ .with(described_class::DEFAULT_LEASE_TIMEOUT, aggregation_schedule.namespace_id)
+ .and_return(nil)
+
+ # Scheduling workers for the first time
+ aggregation_schedule.schedule_root_storage_statistics
+
+ # Executing again, this time workers should not be scheduled
+ # due to the lease not been released.
+ aggregation_schedule.schedule_root_storage_statistics
+ end
end
context 'with a personalized lease timeout' do
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index 22df19d943f..a771d1bf27f 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -101,6 +101,15 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do
is_expected.to eq(:error)
end
+ Gitlab::HTTP::HTTP_ERRORS.each do |http_error|
+ it "sets commit status to :error with a #{http_error.name} error" do
+ WebMock.stub_request(:get, commit_status_path)
+ .to_raise(http_error)
+
+ is_expected.to eq(:error)
+ end
+ end
+
{
"killed" => :canceled,
"failure" => :failed,
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 5d7d6c34e67..d33bbb0470f 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -142,235 +142,6 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
end
end
- describe '#kubernetes_namespace_for' do
- subject { service.kubernetes_namespace_for(project) }
-
- shared_examples 'a correctly formatted namespace' do
- it 'returns a valid Kubernetes namespace name' do
- expect(subject).to match(Gitlab::Regex.kubernetes_namespace_regex)
- expect(subject).to eq(expected_namespace)
- end
- end
-
- it_behaves_like 'a correctly formatted namespace' do
- let(:expected_namespace) { service.send(:default_namespace) }
- end
-
- context 'when the project path contains forbidden characters' do
- before do
- project.path = '-a_Strange.Path--forSure'
- end
-
- it_behaves_like 'a correctly formatted namespace' do
- let(:expected_namespace) { "a-strange-path--forsure-#{project.id}" }
- end
- end
-
- context 'when namespace is specified' do
- before do
- service.namespace = 'my-namespace'
- end
-
- it_behaves_like 'a correctly formatted namespace' do
- let(:expected_namespace) { 'my-namespace' }
- end
- end
-
- context 'when service is not assigned to project' do
- before do
- service.project = nil
- end
-
- it 'does not return namespace' do
- is_expected.to be_nil
- end
- end
- end
-
- describe '#test' do
- let(:discovery_url) { 'https://kubernetes.example.com/api/v1' }
-
- before do
- stub_kubeclient_discover(service.api_url)
- end
-
- context 'with path prefix in api_url' do
- let(:discovery_url) { 'https://kubernetes.example.com/prefix/api/v1' }
-
- it 'tests with the prefix' do
- service.api_url = 'https://kubernetes.example.com/prefix'
- stub_kubeclient_discover(service.api_url)
-
- expect(service.test[:success]).to be_truthy
- expect(WebMock).to have_requested(:get, discovery_url).once
- end
- end
-
- context 'with custom CA certificate' do
- it 'is added to the certificate store' do
- service.ca_pem = "CA PEM DATA"
-
- cert = double("certificate")
- expect(OpenSSL::X509::Certificate).to receive(:new).with(service.ca_pem).and_return(cert)
- expect_any_instance_of(OpenSSL::X509::Store).to receive(:add_cert).with(cert)
-
- expect(service.test[:success]).to be_truthy
- expect(WebMock).to have_requested(:get, discovery_url).once
- end
- end
-
- context 'success' do
- it 'reads the discovery endpoint' do
- expect(service.test[:success]).to be_truthy
- expect(WebMock).to have_requested(:get, discovery_url).once
- end
- end
-
- context 'failure' do
- it 'fails to read the discovery endpoint' do
- WebMock.stub_request(:get, service.api_url + '/api/v1').to_return(status: 404)
-
- expect(service.test[:success]).to be_falsy
- expect(WebMock).to have_requested(:get, discovery_url).once
- end
- end
- end
-
- describe '#predefined_variable' do
- let(:kubeconfig) do
- config_file = expand_fixture_path('config/kubeconfig.yml')
- config = YAML.load(File.read(config_file))
- config.dig('users', 0, 'user')['token'] = 'token'
- config.dig('contexts', 0, 'context')['namespace'] = namespace
- config.dig('clusters', 0, 'cluster')['certificate-authority-data'] =
- Base64.strict_encode64('CA PEM DATA')
-
- YAML.dump(config)
- end
-
- before do
- subject.api_url = 'https://kube.domain.com'
- subject.token = 'token'
- subject.ca_pem = 'CA PEM DATA'
- subject.project = project
- end
-
- shared_examples 'setting variables' do
- it 'sets the variables' do
- expect(subject.predefined_variables(project: project)).to include(
- { key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
- { key: 'KUBE_TOKEN', value: 'token', public: false, masked: true },
- { key: 'KUBE_NAMESPACE', value: namespace, public: true },
- { key: 'KUBECONFIG', value: kubeconfig, public: false, file: true },
- { key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true },
- { key: 'KUBE_CA_PEM_FILE', value: 'CA PEM DATA', public: true, file: true }
- )
- end
- end
-
- context 'namespace is provided' do
- let(:namespace) { 'my-project' }
-
- before do
- subject.namespace = namespace
- end
-
- it_behaves_like 'setting variables'
- end
-
- context 'no namespace provided' do
- let(:namespace) { subject.kubernetes_namespace_for(project) }
-
- it_behaves_like 'setting variables'
-
- it 'sets the KUBE_NAMESPACE' do
- kube_namespace = subject.predefined_variables(project: project).find { |h| h[:key] == 'KUBE_NAMESPACE' }
-
- expect(kube_namespace).not_to be_nil
- expect(kube_namespace[:value]).to match(/\A#{Gitlab::PathRegex::PATH_REGEX_STR}-\d+\z/)
- end
- end
- end
-
- describe '#terminals' do
- let(:environment) { build(:environment, project: project, name: "env", slug: "env-000000") }
-
- subject { service.terminals(environment) }
-
- context 'with invalid pods' do
- it 'returns no terminals' do
- stub_reactive_cache(service, pods: [{ "bad" => "pod" }])
-
- is_expected.to be_empty
- end
- end
-
- context 'with valid pods' do
- let(:pod) { kube_pod(environment_slug: environment.slug, namespace: service.kubernetes_namespace_for(project), project_slug: project.full_path_slug) }
- let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
- let(:terminals) { kube_terminals(service, pod) }
-
- before do
- stub_reactive_cache(
- service,
- pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
- )
- end
-
- it 'returns terminals' do
- is_expected.to eq(terminals + terminals)
- end
-
- it 'uses max session time from settings' do
- stub_application_setting(terminal_max_session_time: 600)
-
- times = subject.map { |terminal| terminal[:max_session_time] }
- expect(times).to eq [600, 600, 600, 600]
- end
- end
- end
-
- describe '#calculate_reactive_cache' do
- subject { service.calculate_reactive_cache }
-
- let(:namespace) { service.kubernetes_namespace_for(project) }
-
- context 'when service is inactive' do
- before do
- service.active = false
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'when kubernetes responds with valid pods' do
- before do
- stub_kubeclient_pods(namespace)
- stub_kubeclient_deployments(namespace) # Used by EE
- end
-
- it { is_expected.to include(pods: [kube_pod]) }
- end
-
- context 'when kubernetes responds with 500s' do
- before do
- stub_kubeclient_pods(namespace, status: 500)
- stub_kubeclient_deployments(namespace, status: 500) # Used by EE
- end
-
- it { expect { subject }.to raise_error(Kubeclient::HttpError) }
- end
-
- context 'when kubernetes responds with 404s' do
- before do
- stub_kubeclient_pods(namespace, status: 404)
- stub_kubeclient_deployments(namespace, status: 404) # Used by EE
- end
-
- it { is_expected.to include(pods: []) }
- end
- end
-
describe "#deprecated?" do
let(:kubernetes_service) { create(:kubernetes_service) }
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 1cb49d83ffa..db3e4902c64 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -135,6 +135,49 @@ describe ProjectStatistics do
expect(statistics.wiki_size).to eq(0)
end
end
+
+ context 'when the column is namespace relatable' do
+ let(:namespace) { create(:group) }
+ let(:project) { create(:project, namespace: namespace) }
+
+ context 'when the feature flag is off' do
+ it 'does not schedule the aggregation worker' do
+ stub_feature_flags(update_statistics_namespace: false, namespace: namespace)
+
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ statistics.refresh!(only: [:lfs_objects_size])
+ end
+ end
+
+ context 'when the feature flag is on' do
+ it 'schedules the aggregation worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async)
+
+ statistics.refresh!(only: [:lfs_objects_size])
+ end
+ end
+
+ context 'when no argument is passed' do
+ it 'schedules the aggregation worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async)
+
+ statistics.refresh!
+ end
+ end
+ end
+
+ context 'when the column is not namespace relatable' do
+ it 'does not schedules an aggregation worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ statistics.refresh!(only: [:commit_count])
+ end
+ end
end
describe '#update_commit_count' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 13da7bd7407..3d967aa4ab8 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1420,12 +1420,13 @@ describe Repository do
source_project: project)
end
- it 'writes merge of source and target to MR merge_ref_path' do
+ it 'writes merge of source SHA and first parent ref to MR merge_ref_path' do
merge_commit_id = repository.merge_to_ref(user,
merge_request.diff_head_sha,
merge_request,
merge_request.merge_ref_path,
- 'Custom message')
+ 'Custom message',
+ merge_request.target_branch_ref)
merge_commit = repository.commit(merge_commit_id)
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
index 656d6f8b50b..54401ec4085 100644
--- a/spec/requests/api/graphql_spec.rb
+++ b/spec/requests/api/graphql_spec.rb
@@ -6,16 +6,6 @@ describe 'GraphQL' do
let(:query) { graphql_query_for('echo', 'text' => 'Hello world' ) }
- context 'graphql is disabled by feature flag' do
- before do
- stub_feature_flags(graphql: false)
- end
-
- it 'does not generate a route for GraphQL' do
- expect { post_graphql(query) }.to raise_error(ActionController::RoutingError)
- end
- end
-
context 'logging' do
shared_examples 'logging a graphql query' do
let(:expected_params) do
@@ -131,4 +121,35 @@ describe 'GraphQL' do
end
end
end
+
+ describe 'testing for Gitaly calls' do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ let(:query) do
+ graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id))
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: user)
+ end
+ end
+
+ context 'when Gitaly is called' do
+ before do
+ allow(Gitlab::GitalyClient).to receive(:get_request_count).and_return(1, 2)
+ end
+
+ it "logs a warning that the 'calls_gitaly' field declaration is missing" do
+ expect(Gitlab::Sentry).to receive(:track_exception).once
+
+ post_graphql(query, current_user: user)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index a82ecb4fd63..ced853caab4 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2033,6 +2033,9 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(202)
expect(RebaseWorker.jobs.size).to eq(1)
+
+ expect(merge_request.reload).to be_rebase_in_progress
+ expect(json_response['rebase_in_progress']).to be(true)
end
it 'returns 403 if the user cannot push to the branch' do
@@ -2043,6 +2046,16 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(403)
end
+
+ it 'returns 409 if a rebase is already in progress' do
+ Sidekiq::Testing.fake! do
+ merge_request.rebase_async(user.id)
+
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user)
+ end
+
+ expect(response).to have_gitlab_http_status(409)
+ end
end
describe 'Time tracking' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index df4758f362b..a2aae257352 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -2482,7 +2482,7 @@ describe API::Projects do
let(:housekeeping) { Projects::HousekeepingService.new(project) }
before do
- allow(Projects::HousekeepingService).to receive(:new).with(project).and_return(housekeeping)
+ allow(Projects::HousekeepingService).to receive(:new).with(project, :gc).and_return(housekeeping)
end
context 'when authenticated as owner' do
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 5548e3fd01a..f5ce3a3570e 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -584,6 +584,34 @@ describe API::Runners do
end
end
+ context 'when valid order_by is provided' do
+ context 'when sort order is not specified' do
+ it 'return jobs in descending order' do
+ get api("/runners/#{project_runner.id}/jobs?order_by=id", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+
+ expect(json_response).to be_an(Array)
+ expect(json_response.length).to eq(2)
+ expect(json_response.first).to include('id' => job_5.id)
+ end
+ end
+
+ context 'when sort order is specified as asc' do
+ it 'return jobs sorted in ascending order' do
+ get api("/runners/#{project_runner.id}/jobs?order_by=id&sort=asc", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+
+ expect(json_response).to be_an(Array)
+ expect(json_response.length).to eq(2)
+ expect(json_response.first).to include('id' => job_4.id)
+ end
+ end
+ end
+
context 'when invalid status is provided' do
it 'return 400' do
get api("/runners/#{project_runner.id}/jobs?status=non-existing", admin)
@@ -591,6 +619,22 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(400)
end
end
+
+ context 'when invalid order_by is provided' do
+ it 'return 400' do
+ get api("/runners/#{project_runner.id}/jobs?order_by=non-existing", admin)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
+ context 'when invalid sort is provided' do
+ it 'return 400' do
+ get api("/runners/#{project_runner.id}/jobs?sort=non-existing", admin)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
end
context "when runner doesn't exist" do
diff --git a/spec/requests/api/user_counts_spec.rb b/spec/requests/api/user_counts_spec.rb
new file mode 100644
index 00000000000..c833bd047e2
--- /dev/null
+++ b/spec/requests/api/user_counts_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::UserCounts do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, title: "Test") }
+
+ describe 'GET /user_counts' do
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get api('/user_counts')
+
+ expect(response.status).to eq(401)
+ end
+ end
+
+ context 'when authenticated' do
+ it 'returns open counts for current user' do
+ get api('/user_counts', user)
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_a Hash
+ expect(json_response['merge_requests']).to eq(1)
+ end
+
+ it 'updates the mr count when a new mr is assigned' do
+ create(:merge_request, source_project: project, author: user, assignees: [user])
+
+ get api('/user_counts', user)
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_a Hash
+ expect(json_response['merge_requests']).to eq(2)
+ end
+ end
+ end
+end
diff --git a/spec/routing/api_routing_spec.rb b/spec/routing/api_routing_spec.rb
deleted file mode 100644
index 3c48ead4ff2..00000000000
--- a/spec/routing/api_routing_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require 'spec_helper'
-
-describe 'api', 'routing' do
- context 'when graphql is disabled' do
- before do
- stub_feature_flags(graphql: false)
- end
-
- it 'does not route to the GraphqlController' do
- expect(post('/api/graphql')).not_to route_to('graphql#execute')
- end
- end
-
- context 'when graphql is enabled' do
- before do
- stub_feature_flags(graphql: true)
- end
-
- it 'routes to the GraphqlController' do
- expect(post('/api/graphql')).to route_to('graphql#execute')
- end
- end
-end
diff --git a/spec/serializers/environment_status_entity_spec.rb b/spec/serializers/environment_status_entity_spec.rb
index 8a6a38fe5f8..f421432e8d6 100644
--- a/spec/serializers/environment_status_entity_spec.rb
+++ b/spec/serializers/environment_status_entity_spec.rb
@@ -9,7 +9,7 @@ describe EnvironmentStatusEntity do
let(:project) { deployment.project }
let(:merge_request) { create(:merge_request, :deployed_review_app, deployment: deployment) }
- let(:environment_status) { EnvironmentStatus.new(environment, merge_request, merge_request.diff_head_sha) }
+ let(:environment_status) { EnvironmentStatus.new(project, environment, merge_request, merge_request.diff_head_sha) }
let(:entity) { described_class.new(environment_status, request: request) }
subject { entity.as_json }
@@ -55,8 +55,14 @@ describe EnvironmentStatusEntity do
before do
project.add_maintainer(user)
allow(deployment).to receive(:prometheus_adapter).and_return(prometheus_adapter)
- allow(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
allow(entity).to receive(:deployment).and_return(deployment)
+
+ expect_next_instance_of(DeploymentMetrics) do |deployment_metrics|
+ allow(deployment_metrics).to receive(:prometheus_adapter).and_return(prometheus_adapter)
+
+ allow(prometheus_adapter).to receive(:query)
+ .with(:deployment, deployment).and_return(simple_metrics)
+ end
end
context 'when deployment succeeded' do
diff --git a/spec/serializers/test_case_entity_spec.rb b/spec/serializers/test_case_entity_spec.rb
index 986c9feb07b..cc5f086ca4e 100644
--- a/spec/serializers/test_case_entity_spec.rb
+++ b/spec/serializers/test_case_entity_spec.rb
@@ -24,7 +24,7 @@ describe TestCaseEntity do
it 'contains correct test case details' do
expect(subject[:status]).to eq('failed')
- expect(subject[:name]).to eq('Test#sum when a is 2 and b is 2 returns summary')
+ expect(subject[:name]).to eq('Test#sum when a is 1 and b is 3 returns summary')
expect(subject[:classname]).to eq('spec.test_spec')
expect(subject[:execution_time]).to eq(2.22)
end
diff --git a/spec/serializers/test_reports_comparer_entity_spec.rb b/spec/serializers/test_reports_comparer_entity_spec.rb
index 59c058fe368..4a951bbbde4 100644
--- a/spec/serializers/test_reports_comparer_entity_spec.rb
+++ b/spec/serializers/test_reports_comparer_entity_spec.rb
@@ -53,13 +53,7 @@ describe TestReportsComparerEntity do
base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
- head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
- end
-
- let(:create_test_case_java_resolved) do
- create_test_case_java_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
- end
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
end
it 'contains correct compared test reports details' do
diff --git a/spec/serializers/test_reports_comparer_serializer_spec.rb b/spec/serializers/test_reports_comparer_serializer_spec.rb
index 9ea86c0dd83..62dc6f486c5 100644
--- a/spec/serializers/test_reports_comparer_serializer_spec.rb
+++ b/spec/serializers/test_reports_comparer_serializer_spec.rb
@@ -44,13 +44,7 @@ describe TestReportsComparerSerializer do
base_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
base_reports.get_suite('junit').add_test_case(create_test_case_java_failed)
head_reports.get_suite('rspec').add_test_case(create_test_case_rspec_success)
- head_reports.get_suite('junit').add_test_case(create_test_case_java_resolved)
- end
-
- let(:create_test_case_java_resolved) do
- create_test_case_java_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
- end
+ head_reports.get_suite('junit').add_test_case(create_test_case_java_success)
end
it 'matches the schema' do
diff --git a/spec/serializers/test_suite_comparer_entity_spec.rb b/spec/serializers/test_suite_comparer_entity_spec.rb
index f61331f53a0..4b2cca2c68c 100644
--- a/spec/serializers/test_suite_comparer_entity_spec.rb
+++ b/spec/serializers/test_suite_comparer_entity_spec.rb
@@ -11,16 +11,10 @@ describe TestSuiteComparerEntity do
let(:test_case_success) { create_test_case_rspec_success }
let(:test_case_failed) { create_test_case_rspec_failed }
- let(:test_case_resolved) do
- create_test_case_rspec_failed.tap do |test_case|
- test_case.instance_variable_set("@status", Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
- end
- end
-
describe '#as_json' do
subject { entity.as_json }
- context 'when head sutie has a newly failed test case which does not exist in base' do
+ context 'when head suite has a newly failed test case which does not exist in base' do
before do
base_suite.add_test_case(test_case_success)
head_suite.add_test_case(test_case_failed)
@@ -41,7 +35,7 @@ describe TestSuiteComparerEntity do
end
end
- context 'when head sutie still has a failed test case which failed in base' do
+ context 'when head suite still has a failed test case which failed in base' do
before do
base_suite.add_test_case(test_case_failed)
head_suite.add_test_case(test_case_failed)
@@ -62,10 +56,10 @@ describe TestSuiteComparerEntity do
end
end
- context 'when head sutie has a success test case which failed in base' do
+ context 'when head suite has a success test case which failed in base' do
before do
base_suite.add_test_case(test_case_failed)
- head_suite.add_test_case(test_case_resolved)
+ head_suite.add_test_case(test_case_success)
end
it 'contains correct compared test suite details' do
@@ -74,13 +68,83 @@ describe TestSuiteComparerEntity do
expect(subject[:summary]).to include(total: 1, resolved: 1, failed: 0)
expect(subject[:new_failures]).to be_empty
subject[:resolved_failures].first.tap do |resolved_failure|
- expect(resolved_failure[:status]).to eq(test_case_resolved.status)
- expect(resolved_failure[:name]).to eq(test_case_resolved.name)
- expect(resolved_failure[:execution_time]).to eq(test_case_resolved.execution_time)
- expect(resolved_failure[:system_output]).to eq(test_case_resolved.system_output)
+ expect(resolved_failure[:status]).to eq(test_case_success.status)
+ expect(resolved_failure[:name]).to eq(test_case_success.name)
+ expect(resolved_failure[:execution_time]).to eq(test_case_success.execution_time)
+ expect(resolved_failure[:system_output]).to eq(test_case_success.system_output)
end
expect(subject[:existing_failures]).to be_empty
end
end
+
+ context 'limits amount of tests returned' do
+ before do
+ stub_const('TestSuiteComparerEntity::DEFAULT_MAX_TESTS', 2)
+ stub_const('TestSuiteComparerEntity::DEFAULT_MIN_TESTS', 1)
+ end
+
+ context 'prefers new over existing and resolved' do
+ before do
+ 3.times { add_new_failure }
+ 3.times { add_existing_failure }
+ 3.times { add_resolved_failure }
+ end
+
+ it 'returns 2 new failures, and 1 of resolved and existing' do
+ expect(subject[:summary]).to include(total: 9, resolved: 3, failed: 6)
+ expect(subject[:new_failures].count).to eq(2)
+ expect(subject[:existing_failures].count).to eq(1)
+ expect(subject[:resolved_failures].count).to eq(1)
+ end
+ end
+
+ context 'prefers existing over resolved' do
+ before do
+ 3.times { add_existing_failure }
+ 3.times { add_resolved_failure }
+ end
+
+ it 'returns 2 existing failures, and 1 resolved' do
+ expect(subject[:summary]).to include(total: 6, resolved: 3, failed: 3)
+ expect(subject[:new_failures].count).to eq(0)
+ expect(subject[:existing_failures].count).to eq(2)
+ expect(subject[:resolved_failures].count).to eq(1)
+ end
+ end
+
+ context 'limits amount of resolved' do
+ before do
+ 3.times { add_resolved_failure }
+ end
+
+ it 'returns 2 resolved failures' do
+ expect(subject[:summary]).to include(total: 3, resolved: 3, failed: 0)
+ expect(subject[:new_failures].count).to eq(0)
+ expect(subject[:existing_failures].count).to eq(0)
+ expect(subject[:resolved_failures].count).to eq(2)
+ end
+ end
+
+ private
+
+ def add_new_failure
+ failed_case = create_test_case_rspec_failed(SecureRandom.hex)
+ head_suite.add_test_case(failed_case)
+ end
+
+ def add_existing_failure
+ failed_case = create_test_case_rspec_failed(SecureRandom.hex)
+ base_suite.add_test_case(failed_case)
+ head_suite.add_test_case(failed_case)
+ end
+
+ def add_resolved_failure
+ case_name = SecureRandom.hex
+ failed_case = create_test_case_rspec_failed(case_name)
+ success_case = create_test_case_rspec_success(case_name)
+ base_suite.add_test_case(failed_case)
+ head_suite.add_test_case(success_case)
+ end
+ end
end
end
diff --git a/spec/services/auto_merge/base_service_spec.rb b/spec/services/auto_merge/base_service_spec.rb
index cd08e0b6f32..a409f21a7e4 100644
--- a/spec/services/auto_merge/base_service_spec.rb
+++ b/spec/services/auto_merge/base_service_spec.rb
@@ -59,6 +59,11 @@ describe AutoMerge::BaseService do
context 'when strategy is merge when pipeline succeeds' do
let(:service) { AutoMerge::MergeWhenPipelineSucceedsService.new(project, user) }
+ before do
+ pipeline = build(:ci_pipeline)
+ allow(merge_request).to receive(:actual_head_pipeline) { pipeline }
+ end
+
it 'sets the auto merge strategy' do
subject
@@ -116,11 +121,7 @@ describe AutoMerge::BaseService do
end
end
- describe '#cancel' do
- subject { service.cancel(merge_request) }
-
- let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
-
+ shared_examples_for 'Canceled or Dropped' do
it 'removes properies from the merge request' do
subject
@@ -168,6 +169,20 @@ describe AutoMerge::BaseService do
it 'does not yield block' do
expect { |b| service.execute(merge_request, &b) }.not_to yield_control
end
+ end
+ end
+
+ describe '#cancel' do
+ subject { service.cancel(merge_request) }
+
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+
+ it_behaves_like 'Canceled or Dropped'
+
+ context 'when failed to save' do
+ before do
+ allow(merge_request).to receive(:save) { false }
+ end
it 'returns error status' do
expect(subject[:status]).to eq(:error)
@@ -175,4 +190,24 @@ describe AutoMerge::BaseService do
end
end
end
+
+ describe '#abort' do
+ subject { service.abort(merge_request, reason) }
+
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+ let(:reason) { 'an error'}
+
+ it_behaves_like 'Canceled or Dropped'
+
+ context 'when failed to save' do
+ before do
+ allow(merge_request).to receive(:save) { false }
+ end
+
+ it 'returns error status' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq("Can't abort the automatic merge")
+ end
+ end
+ end
end
diff --git a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
index a20bf8e17e4..931b52470c4 100644
--- a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
@@ -64,8 +64,11 @@ describe AutoMerge::MergeWhenPipelineSucceedsService do
end
it 'creates a system note' do
+ pipeline = build(:ci_pipeline)
+ allow(merge_request).to receive(:actual_head_pipeline) { pipeline }
+
note = merge_request.notes.last
- expect(note.note).to match %r{enabled an automatic merge when the pipeline for (\w+/\w+@)?\h{8}}
+ expect(note.note).to match "enabled an automatic merge when the pipeline for #{pipeline.sha}"
end
end
@@ -174,6 +177,17 @@ describe AutoMerge::MergeWhenPipelineSucceedsService do
end
end
+ describe "#abort" do
+ before do
+ service.abort(mr_merge_if_green_enabled, 'an error')
+ end
+
+ it 'posts a system note' do
+ note = mr_merge_if_green_enabled.notes.last
+ expect(note.note).to include 'aborted the automatic merge'
+ end
+ end
+
describe 'pipeline integration' do
context 'when there are multiple stages in the pipeline' do
let(:ref) { mr_merge_if_green_enabled.source_branch }
diff --git a/spec/services/auto_merge_service_spec.rb b/spec/services/auto_merge_service_spec.rb
index 93a22e60498..50dfc49a59c 100644
--- a/spec/services/auto_merge_service_spec.rb
+++ b/spec/services/auto_merge_service_spec.rb
@@ -161,4 +161,29 @@ describe AutoMergeService do
end
end
end
+
+ describe '#abort' do
+ subject { service.abort(merge_request, error) }
+
+ let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+ let(:error) { 'an error' }
+
+ it 'delegates to a relevant service instance' do
+ expect_next_instance_of(AutoMerge::MergeWhenPipelineSucceedsService) do |service|
+ expect(service).to receive(:abort).with(merge_request, error)
+ end
+
+ subject
+ end
+
+ context 'when auto merge is not enabled' do
+ let(:merge_request) { create(:merge_request) }
+
+ it 'returns error' do
+ expect(subject[:message]).to eq("Can't abort the automatic merge")
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:http_status]).to eq(406)
+ end
+ end
+ end
end
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index aebc5ba2874..3d2d4b5f216 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -11,343 +11,371 @@ describe Issuable::BulkUpdateService do
.reverse_merge(issuable_ids: Array(issuables).map(&:id).join(','))
type = Array(issuables).first.model_name.param_key
- Issuable::BulkUpdateService.new(project, user, bulk_update_params).execute(type)
+ Issuable::BulkUpdateService.new(user, bulk_update_params).execute(type)
end
- describe 'close issues' do
- let(:issues) { create_list(:issue, 2, project: project) }
-
- it 'succeeds and returns the correct number of issues updated' do
- result = bulk_update(issues, state_event: 'close')
+ shared_examples 'updates milestones' do
+ it 'succeeds' do
+ result = bulk_update(issues, milestone_id: milestone.id)
expect(result[:success]).to be_truthy
expect(result[:count]).to eq(issues.count)
end
- it 'closes all the issues passed' do
- bulk_update(issues, state_event: 'close')
+ it 'updates the issues milestone' do
+ bulk_update(issues, milestone_id: milestone.id)
- expect(project.issues.opened).to be_empty
- expect(project.issues.closed).not_to be_empty
+ issues.each do |issue|
+ expect(issue.reload.milestone).to eq(milestone)
+ end
end
+ end
- context 'when issue for a different project is created' do
- let(:private_project) { create(:project, :private) }
- let(:issue) { create(:issue, project: private_project, author: user) }
+ context 'with project issues' do
+ describe 'close issues' do
+ let(:issues) { create_list(:issue, 2, project: project) }
- context 'when user has access to the project' do
- it 'closes all issues passed' do
- private_project.add_maintainer(user)
+ it 'succeeds and returns the correct number of issues updated' do
+ result = bulk_update(issues, state_event: 'close')
- bulk_update(issues + [issue], state_event: 'close')
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(issues.count)
+ end
- expect(project.issues.opened).to be_empty
- expect(project.issues.closed).not_to be_empty
- expect(private_project.issues.closed).not_to be_empty
- end
+ it 'closes all the issues passed' do
+ bulk_update(issues, state_event: 'close')
+
+ expect(project.issues.opened).to be_empty
+ expect(project.issues.closed).not_to be_empty
end
- context 'when user does not have access to project' do
- it 'only closes all issues that the user has access to' do
- bulk_update(issues + [issue], state_event: 'close')
+ context 'when issue for a different project is created' do
+ let(:private_project) { create(:project, :private) }
+ let(:issue) { create(:issue, project: private_project, author: user) }
+
+ context 'when user has access to the project' do
+ it 'closes all issues passed' do
+ private_project.add_maintainer(user)
+
+ bulk_update(issues + [issue], state_event: 'close')
+
+ expect(project.issues.opened).to be_empty
+ expect(project.issues.closed).not_to be_empty
+ expect(private_project.issues.closed).not_to be_empty
+ end
+ end
+
+ context 'when user does not have access to project' do
+ it 'only closes all issues that the user has access to' do
+ bulk_update(issues + [issue], state_event: 'close')
- expect(project.issues.opened).to be_empty
- expect(project.issues.closed).not_to be_empty
- expect(private_project.issues.closed).to be_empty
+ expect(project.issues.opened).to be_empty
+ expect(project.issues.closed).not_to be_empty
+ expect(private_project.issues.closed).to be_empty
+ end
end
end
end
- end
- describe 'reopen issues' do
- let(:issues) { create_list(:closed_issue, 2, project: project) }
+ describe 'reopen issues' do
+ let(:issues) { create_list(:closed_issue, 2, project: project) }
- it 'succeeds and returns the correct number of issues updated' do
- result = bulk_update(issues, state_event: 'reopen')
+ it 'succeeds and returns the correct number of issues updated' do
+ result = bulk_update(issues, state_event: 'reopen')
- expect(result[:success]).to be_truthy
- expect(result[:count]).to eq(issues.count)
- end
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(issues.count)
+ end
- it 'reopens all the issues passed' do
- bulk_update(issues, state_event: 'reopen')
+ it 'reopens all the issues passed' do
+ bulk_update(issues, state_event: 'reopen')
- expect(project.issues.closed).to be_empty
- expect(project.issues.opened).not_to be_empty
+ expect(project.issues.closed).to be_empty
+ expect(project.issues.opened).not_to be_empty
+ end
end
- end
- describe 'updating merge request assignee' do
- let(:merge_request) { create(:merge_request, target_project: project, source_project: project, assignees: [user]) }
+ describe 'updating merge request assignee' do
+ let(:merge_request) { create(:merge_request, target_project: project, source_project: project, assignees: [user]) }
- context 'when the new assignee ID is a valid user' do
- it 'succeeds' do
- new_assignee = create(:user)
- project.add_developer(new_assignee)
+ context 'when the new assignee ID is a valid user' do
+ it 'succeeds' do
+ new_assignee = create(:user)
+ project.add_developer(new_assignee)
- result = bulk_update(merge_request, assignee_ids: [user.id, new_assignee.id])
+ result = bulk_update(merge_request, assignee_ids: [user.id, new_assignee.id])
- expect(result[:success]).to be_truthy
- expect(result[:count]).to eq(1)
- end
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(1)
+ end
- it 'updates the assignee to the user ID passed' do
- assignee = create(:user)
- project.add_developer(assignee)
+ it 'updates the assignee to the user ID passed' do
+ assignee = create(:user)
+ project.add_developer(assignee)
- expect { bulk_update(merge_request, assignee_ids: [assignee.id]) }
- .to change { merge_request.reload.assignee_ids }.from([user.id]).to([assignee.id])
+ expect { bulk_update(merge_request, assignee_ids: [assignee.id]) }
+ .to change { merge_request.reload.assignee_ids }.from([user.id]).to([assignee.id])
+ end
end
- end
- context "when the new assignee ID is #{IssuableFinder::NONE}" do
- it 'unassigns the issues' do
- expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::NONE]) }
- .to change { merge_request.reload.assignee_ids }.to([])
+ context "when the new assignee ID is #{IssuableFinder::NONE}" do
+ it 'unassigns the issues' do
+ expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::NONE]) }
+ .to change { merge_request.reload.assignee_ids }.to([])
+ end
end
- end
- context 'when the new assignee ID is not present' do
- it 'does not unassign' do
- expect { bulk_update(merge_request, assignee_ids: []) }
- .not_to change { merge_request.reload.assignee_ids }
+ context 'when the new assignee ID is not present' do
+ it 'does not unassign' do
+ expect { bulk_update(merge_request, assignee_ids: []) }
+ .not_to change { merge_request.reload.assignee_ids }
+ end
end
end
- end
- describe 'updating issue assignee' do
- let(:issue) { create(:issue, project: project, assignees: [user]) }
+ describe 'updating issue assignee' do
+ let(:issue) { create(:issue, project: project, assignees: [user]) }
- context 'when the new assignee ID is a valid user' do
- it 'succeeds' do
- new_assignee = create(:user)
- project.add_developer(new_assignee)
+ context 'when the new assignee ID is a valid user' do
+ it 'succeeds' do
+ new_assignee = create(:user)
+ project.add_developer(new_assignee)
- result = bulk_update(issue, assignee_ids: [new_assignee.id])
+ result = bulk_update(issue, assignee_ids: [new_assignee.id])
- expect(result[:success]).to be_truthy
- expect(result[:count]).to eq(1)
- end
+ expect(result[:success]).to be_truthy
+ expect(result[:count]).to eq(1)
+ end
- it 'updates the assignee to the user ID passed' do
- assignee = create(:user)
- project.add_developer(assignee)
- expect { bulk_update(issue, assignee_ids: [assignee.id]) }
- .to change { issue.reload.assignees.first }.from(user).to(assignee)
+ it 'updates the assignee to the user ID passed' do
+ assignee = create(:user)
+ project.add_developer(assignee)
+ expect { bulk_update(issue, assignee_ids: [assignee.id]) }
+ .to change { issue.reload.assignees.first }.from(user).to(assignee)
+ end
end
- end
- context "when the new assignee ID is #{IssuableFinder::NONE}" do
- it "unassigns the issues" do
- expect { bulk_update(issue, assignee_ids: [IssuableFinder::NONE.to_s]) }
- .to change { issue.reload.assignees.count }.from(1).to(0)
+ context "when the new assignee ID is #{IssuableFinder::NONE}" do
+ it "unassigns the issues" do
+ expect { bulk_update(issue, assignee_ids: [IssuableFinder::NONE.to_s]) }
+ .to change { issue.reload.assignees.count }.from(1).to(0)
+ end
end
- end
- context 'when the new assignee ID is not present' do
- it 'does not unassign' do
- expect { bulk_update(issue, assignee_ids: []) }
- .not_to change { issue.reload.assignees }
+ context 'when the new assignee ID is not present' do
+ it 'does not unassign' do
+ expect { bulk_update(issue, assignee_ids: []) }
+ .not_to change { issue.reload.assignees }
+ end
end
end
- end
-
- describe 'updating milestones' do
- let(:issue) { create(:issue, project: project) }
- let(:milestone) { create(:milestone, project: project) }
- it 'succeeds' do
- result = bulk_update(issue, milestone_id: milestone.id)
+ describe 'updating milestones' do
+ let(:issues) { [create(:issue, project: project)] }
+ let(:milestone) { create(:milestone, project: project) }
- expect(result[:success]).to be_truthy
- expect(result[:count]).to eq(1)
+ it_behaves_like 'updates milestones'
end
- it 'updates the issue milestone' do
- expect { bulk_update(issue, milestone_id: milestone.id) }
- .to change { issue.reload.milestone }.from(nil).to(milestone)
- end
- end
-
- describe 'updating labels' do
- def create_issue_with_labels(labels)
- create(:labeled_issue, project: project, labels: labels)
- end
+ describe 'updating labels' do
+ def create_issue_with_labels(labels)
+ create(:labeled_issue, project: project, labels: labels)
+ end
- let(:bug) { create(:label, project: project) }
- let(:regression) { create(:label, project: project) }
- let(:merge_requests) { create(:label, project: project) }
-
- let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
- let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
- let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
- let(:issue_no_labels) { create(:issue, project: project) }
- let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }
-
- let(:labels) { [] }
- let(:add_labels) { [] }
- let(:remove_labels) { [] }
-
- let(:bulk_update_params) do
- {
- label_ids: labels.map(&:id),
- add_label_ids: add_labels.map(&:id),
- remove_label_ids: remove_labels.map(&:id)
- }
- end
+ let(:bug) { create(:label, project: project) }
+ let(:regression) { create(:label, project: project) }
+ let(:merge_requests) { create(:label, project: project) }
- before do
- bulk_update(issues, bulk_update_params)
- end
+ let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
+ let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
+ let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
+ let(:issue_no_labels) { create(:issue, project: project) }
+ let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }
- context 'when label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_no_labels] }
- let(:labels) { [bug, regression] }
+ let(:labels) { [] }
+ let(:add_labels) { [] }
+ let(:remove_labels) { [] }
- it 'updates the labels of all issues passed to the labels passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
+ let(:bulk_update_params) do
+ {
+ label_ids: labels.map(&:id),
+ add_label_ids: add_labels.map(&:id),
+ remove_label_ids: remove_labels.map(&:id)
+ }
end
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ before do
+ bulk_update(issues, bulk_update_params)
end
- context 'when those label IDs are empty' do
- let(:labels) { [] }
+ context 'when label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_no_labels] }
+ let(:labels) { [bug, regression] }
- it 'updates the issues passed to have no labels' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+ it 'updates the labels of all issues passed to the labels passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
end
- end
- end
- context 'when add_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:add_labels) { [bug, regression, merge_requests] }
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
- it 'adds those label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
- end
+ context 'when those label IDs are empty' do
+ let(:labels) { [] }
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ it 'updates the issues passed to have no labels' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+ end
+ end
end
- end
- context 'when remove_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:remove_labels) { [bug, regression, merge_requests] }
+ context 'when add_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:add_labels) { [bug, regression, merge_requests] }
- it 'removes those label IDs from all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
- end
+ it 'adds those label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
+ end
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
end
- end
- context 'when add_label_ids and remove_label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
- let(:add_labels) { [bug] }
- let(:remove_labels) { [merge_requests] }
+ context 'when remove_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:remove_labels) { [bug, regression, merge_requests] }
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
- end
+ it 'removes those label IDs from all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
+ end
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
end
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
- end
- end
+ context 'when add_label_ids and remove_label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
+ let(:add_labels) { [bug] }
+ let(:remove_labels) { [merge_requests] }
- context 'when add_label_ids and label_ids are passed' do
- let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] }
- let(:labels) { [merge_requests] }
- let(:add_labels) { [regression] }
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id))
- end
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ end
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
end
- it 'does not update issues not passed in' do
- expect(issue_no_labels.label_ids).to be_empty
- end
- end
+ context 'when add_label_ids and label_ids are passed' do
+ let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests] }
+ let(:labels) { [merge_requests] }
+ let(:add_labels) { [regression] }
- context 'when remove_label_ids and label_ids are passed' do
- let(:issues) { [issue_no_labels, issue_bug_and_regression] }
- let(:labels) { [merge_requests] }
- let(:remove_labels) { [regression] }
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(regression.id))
+ end
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
- end
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ it 'does not update issues not passed in' do
+ expect(issue_no_labels.label_ids).to be_empty
+ end
end
- it 'does not update issues not passed in' do
- expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id)
- end
- end
+ context 'when remove_label_ids and label_ids are passed' do
+ let(:issues) { [issue_no_labels, issue_bug_and_regression] }
+ let(:labels) { [merge_requests] }
+ let(:remove_labels) { [regression] }
- context 'when add_label_ids, remove_label_ids, and label_ids are passed' do
- let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] }
- let(:labels) { [regression] }
- let(:add_labels) { [bug] }
- let(:remove_labels) { [merge_requests] }
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
+ end
- it 'adds the label IDs to all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
- end
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ end
- it 'removes the label IDs from all issues passed' do
- expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ it 'does not update issues not passed in' do
+ expect(issue_all_labels.label_ids).to contain_exactly(bug.id, regression.id, merge_requests.id)
+ end
end
- it 'ignores the label IDs parameter' do
- expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
- end
+ context 'when add_label_ids, remove_label_ids, and label_ids are passed' do
+ let(:issues) { [issue_bug_and_merge_requests, issue_no_labels] }
+ let(:labels) { [regression] }
+ let(:add_labels) { [bug] }
+ let(:remove_labels) { [merge_requests] }
- it 'does not update issues not passed in' do
- expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ it 'adds the label IDs to all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
+ end
+
+ it 'removes the label IDs from all issues passed' do
+ expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(merge_requests.id)
+ end
+
+ it 'ignores the label IDs parameter' do
+ expect(issues.map(&:reload).map(&:label_ids).flatten).not_to include(regression.id)
+ end
+
+ it 'does not update issues not passed in' do
+ expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
+ end
end
end
- end
- describe 'subscribe to issues' do
- let(:issues) { create_list(:issue, 2, project: project) }
+ describe 'subscribe to issues' do
+ let(:issues) { create_list(:issue, 2, project: project) }
- it 'subscribes the given user' do
- bulk_update(issues, subscription_event: 'subscribe')
+ it 'subscribes the given user' do
+ bulk_update(issues, subscription_event: 'subscribe')
- expect(issues).to all(be_subscribed(user, project))
+ expect(issues).to all(be_subscribed(user, project))
+ end
end
- end
- describe 'unsubscribe from issues' do
- let(:issues) do
- create_list(:closed_issue, 2, project: project) do |issue|
- issue.subscriptions.create(user: user, project: project, subscribed: true)
+ describe 'unsubscribe from issues' do
+ let(:issues) do
+ create_list(:closed_issue, 2, project: project) do |issue|
+ issue.subscriptions.create(user: user, project: project, subscribed: true)
+ end
+ end
+
+ it 'unsubscribes the given user' do
+ bulk_update(issues, subscription_event: 'unsubscribe')
+
+ issues.each do |issue|
+ expect(issue).not_to be_subscribed(user, project)
+ end
end
end
+ end
- it 'unsubscribes the given user' do
- bulk_update(issues, subscription_event: 'unsubscribe')
+ context 'with group issues' do
+ let(:group) { create(:group) }
- issues.each do |issue|
- expect(issue).not_to be_subscribed(user, project)
+ context 'updating milestone' do
+ let(:milestone) { create(:milestone, group: group) }
+ let(:project1) { create(:project, :repository, group: group) }
+ let(:project2) { create(:project, :repository, group: group) }
+ let(:issue1) { create(:issue, project: project1) }
+ let(:issue2) { create(:issue, project: project2) }
+ let(:issues) { [issue1, issue2] }
+
+ before do
+ group.add_maintainer(user)
end
+
+ it_behaves_like 'updates milestones'
end
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 28fa5d12d9c..468e7c286d5 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -480,6 +480,22 @@ describe Issues::UpdateService, :mailer do
update_issue(description: "- [x] Task 1\n- [X] Task 2")
end
+ it 'does not check for spam on task status change' do
+ params = {
+ update_task: {
+ index: 1,
+ checked: false,
+ line_source: '- [x] Task 1',
+ line_number: 1
+ }
+ }
+ service = described_class.new(project, user, params)
+
+ expect(service).not_to receive(:spam_check)
+
+ service.execute(issue)
+ end
+
it 'creates system note about task status change' do
note1 = find_note('marked the task **Task 1** as completed')
note2 = find_note('marked the task **Task 2** as completed')
diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb
index 61f99f82a76..14012b4ea2d 100644
--- a/spec/services/merge_requests/merge_to_ref_service_spec.rb
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -22,7 +22,6 @@ describe MergeRequests::MergeToRefService do
shared_examples_for 'successfully merges to ref with merge method' do
it 'writes commit to merge ref' do
repository = project.repository
- target_ref = merge_request.merge_ref_path
expect(repository.ref_exists?(target_ref)).to be(false)
@@ -33,7 +32,7 @@ describe MergeRequests::MergeToRefService do
expect(result[:status]).to eq(:success)
expect(result[:commit_id]).to be_present
expect(result[:source_id]).to eq(merge_request.source_branch_sha)
- expect(result[:target_id]).to eq(merge_request.target_branch_sha)
+ expect(result[:target_id]).to eq(repository.commit(first_parent_ref).sha)
expect(repository.ref_exists?(target_ref)).to be(true)
expect(ref_head.id).to eq(result[:commit_id])
end
@@ -74,17 +73,22 @@ describe MergeRequests::MergeToRefService do
describe '#execute' do
let(:service) do
- described_class.new(project, user, commit_message: 'Awesome message',
- should_remove_source_branch: true)
+ described_class.new(project, user, **params)
end
+ let(:params) { { commit_message: 'Awesome message', should_remove_source_branch: true } }
+
def process_merge_to_ref
perform_enqueued_jobs do
service.execute(merge_request)
end
end
- it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/heads/master' }
+ let(:target_ref) { merge_request.merge_ref_path }
+ end
+
it_behaves_like 'successfully evaluates pre-condition checks'
context 'commit history comparison with regular MergeService' do
@@ -129,14 +133,22 @@ describe MergeRequests::MergeToRefService do
context 'when semi-linear merge method' do
let(:merge_method) { :rebase_merge }
- it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/heads/master' }
+ let(:target_ref) { merge_request.merge_ref_path }
+ end
+
it_behaves_like 'successfully evaluates pre-condition checks'
end
context 'when fast-forward merge method' do
let(:merge_method) { :ff }
- it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/heads/master' }
+ let(:target_ref) { merge_request.merge_ref_path }
+ end
+
it_behaves_like 'successfully evaluates pre-condition checks'
end
@@ -178,5 +190,43 @@ describe MergeRequests::MergeToRefService do
it { expect(todo).not_to be_done }
end
+
+ context 'when target ref is passed as a parameter' do
+ let(:params) { { commit_message: 'merge train', target_ref: target_ref } }
+
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/heads/master' }
+ let(:target_ref) { 'refs/merge-requests/1/train' }
+ end
+ end
+
+ describe 'cascading merge refs' do
+ set(:project) { create(:project, :repository) }
+ let(:params) { { commit_message: 'Cascading merge', first_parent_ref: first_parent_ref, target_ref: target_ref } }
+
+ context 'when first merge happens' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project, source_branch: 'feature',
+ target_project: project, target_branch: 'master')
+ end
+
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/heads/master' }
+ let(:target_ref) { 'refs/merge-requests/1/train' }
+ end
+
+ context 'when second merge happens' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project, source_branch: 'improve/awesome',
+ target_project: project, target_branch: 'master')
+ end
+
+ it_behaves_like 'successfully merges to ref with merge method' do
+ let(:first_parent_ref) { 'refs/merge-requests/1/train' }
+ let(:target_ref) { 'refs/merge-requests/2/train' }
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 7e2f03d1097..ee9caaf2f47 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -6,10 +6,12 @@ describe MergeRequests::RebaseService do
include ProjectForksHelper
let(:user) { create(:user) }
+ let(:rebase_jid) { 'fake-rebase-jid' }
let(:merge_request) do
- create(:merge_request,
+ create :merge_request,
source_branch: 'feature_conflict',
- target_branch: 'master')
+ target_branch: 'master',
+ rebase_jid: rebase_jid
end
let(:project) { merge_request.project }
let(:repository) { project.repository.raw }
@@ -23,11 +25,11 @@ describe MergeRequests::RebaseService do
describe '#execute' do
context 'when another rebase is already in progress' do
before do
- allow(merge_request).to receive(:rebase_in_progress?).and_return(true)
+ allow(merge_request).to receive(:gitaly_rebase_in_progress?).and_return(true)
end
it 'saves the error message' do
- subject.execute(merge_request)
+ service.execute(merge_request)
expect(merge_request.reload.merge_error).to eq 'Rebase task canceled: Another rebase is already in progress'
end
@@ -36,6 +38,13 @@ describe MergeRequests::RebaseService do
expect(service.execute(merge_request)).to match(status: :error,
message: described_class::REBASE_ERROR)
end
+
+ it 'clears rebase_jid' do
+ expect { service.execute(merge_request) }
+ .to change { merge_request.rebase_jid }
+ .from(rebase_jid)
+ .to(nil)
+ end
end
shared_examples 'sequence of failure and success' do
@@ -43,14 +52,19 @@ describe MergeRequests::RebaseService do
allow(repository).to receive(:gitaly_operation_client).and_raise('Something went wrong')
service.execute(merge_request)
+ merge_request.reload
- expect(merge_request.reload.merge_error).to eq described_class::REBASE_ERROR
+ expect(merge_request.reload.merge_error).to eq(described_class::REBASE_ERROR)
+ expect(merge_request.rebase_jid).to eq(nil)
allow(repository).to receive(:gitaly_operation_client).and_call_original
+ merge_request.update!(rebase_jid: rebase_jid)
service.execute(merge_request)
+ merge_request.reload
- expect(merge_request.reload.merge_error).to eq nil
+ expect(merge_request.merge_error).to eq(nil)
+ expect(merge_request.rebase_jid).to eq(nil)
end
end
@@ -72,7 +86,7 @@ describe MergeRequests::RebaseService do
it 'saves a generic error message' do
subject.execute(merge_request)
- expect(merge_request.reload.merge_error).to eq described_class::REBASE_ERROR
+ expect(merge_request.reload.merge_error).to eq(described_class::REBASE_ERROR)
end
it 'returns an error' do
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 2a2547f9400..157cfc46e69 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -332,7 +332,7 @@ describe SystemNoteService do
create(:merge_request, source_project: project, target_project: project)
end
- subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, noteable.diff_head_commit) }
+ subject { described_class.merge_when_pipeline_succeeds(noteable, project, author, pipeline.sha) }
it_behaves_like 'a system note' do
let(:action) { 'merge' }
@@ -359,6 +359,22 @@ describe SystemNoteService do
end
end
+ describe '.abort_merge_when_pipeline_succeeds' do
+ let(:noteable) do
+ create(:merge_request, source_project: project, target_project: project)
+ end
+
+ subject { described_class.abort_merge_when_pipeline_succeeds(noteable, project, author, 'merge request was closed') }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'merge' }
+ end
+
+ it "posts the 'merge when pipeline succeeds' system note" do
+ expect(subject.note).to eq "aborted the automatic merge because merge request was closed"
+ end
+ end
+
describe '.change_title' do
let(:noteable) { create(:issue, project: project, title: 'Lorem ipsum') }
@@ -1175,16 +1191,30 @@ describe SystemNoteService do
end
it 'links to the diff in the system note' do
- expect(subject.note).to include('version 1')
-
diff_id = merge_request.merge_request_diff.id
line_code = change_position.line_code(project.repository)
- expect(subject.note).to include(diffs_project_merge_request_path(project, merge_request, diff_id: diff_id, anchor: line_code))
+ link = diffs_project_merge_request_path(project, merge_request, diff_id: diff_id, anchor: line_code)
+
+ expect(subject.note).to eq("changed this line in [version 1 of the diff](#{link})")
+ end
+
+ context 'discussion is on an image' do
+ let(:discussion) { create(:image_diff_note_on_merge_request, project: project).to_discussion }
+
+ it 'links to the diff in the system note' do
+ diff_id = merge_request.merge_request_diff.id
+ file_hash = change_position.file_hash
+ link = diffs_project_merge_request_path(project, merge_request, diff_id: diff_id, anchor: file_hash)
+
+ expect(subject.note).to eq("changed this file in [version 1 of the diff](#{link})")
+ end
end
end
- context 'when the change_position is invalid for the discussion' do
- let(:change_position) { project.commit(sample_commit.id) }
+ context 'when the change_position does not point to a valid version' do
+ before do
+ allow(merge_request).to receive(:version_params_for).and_return(nil)
+ end
it 'creates a new note in the discussion' do
# we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded.
diff --git a/spec/support/helpers/devise_helpers.rb b/spec/support/helpers/devise_helpers.rb
index d32bc2424c0..fb2a110422a 100644
--- a/spec/support/helpers/devise_helpers.rb
+++ b/spec/support/helpers/devise_helpers.rb
@@ -21,4 +21,16 @@ module DeviseHelpers
context.env
end
end
+
+ def with_omniauth_full_host(&block)
+ # The OmniAuth `full_host` parameter doesn't get set correctly (it gets set to something like `http://localhost`
+ # here), and causes integration tests to fail with 404s. We set the `full_host` by removing the request path (and
+ # anything after it) from the request URI.
+ omniauth_config_full_host = OmniAuth.config.full_host
+ OmniAuth.config.full_host = ->(request) { ActionDispatch::Request.new(request).base_url }
+
+ yield
+
+ OmniAuth.config.full_host = omniauth_config_full_host
+ end
end
diff --git a/spec/support/helpers/fake_u2f_device.rb b/spec/support/helpers/fake_u2f_device.rb
index a7605cd483a..22cd8152d77 100644
--- a/spec/support/helpers/fake_u2f_device.rb
+++ b/spec/support/helpers/fake_u2f_device.rb
@@ -32,6 +32,10 @@ class FakeU2fDevice
")
end
+ def fake_u2f_authentication
+ @page.execute_script("window.gl.u2fAuthenticate.renderAuthenticated('abc');")
+ end
+
private
def u2f_device(app_id)
diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb
index cd49bb148f2..c83860d7b51 100644
--- a/spec/support/helpers/git_http_helpers.rb
+++ b/spec/support/helpers/git_http_helpers.rb
@@ -1,4 +1,8 @@
+require_relative 'workhorse_helpers'
+
module GitHttpHelpers
+ include WorkhorseHelpers
+
def clone_get(project, options = {})
get "/#{project}/info/refs", params: { service: 'git-upload-pack' }, headers: auth_env(*options.values_at(:user, :password, :spnego_request_token))
end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 0bb2d2510c2..0cb99b4e087 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -87,12 +87,17 @@ module LoginHelpers
click_link "oauth-login-#{provider}"
end
+ def fake_successful_u2f_authentication
+ allow(U2fRegistration).to receive(:authenticate).and_return(true)
+ FakeU2fDevice.new(page, nil).fake_u2f_authentication
+ end
+
def mock_auth_hash_with_saml_xml(provider, uid, email, saml_response)
response_object = { document: saml_xml(saml_response) }
mock_auth_hash(provider, uid, email, response_object: response_object)
end
- def mock_auth_hash(provider, uid, email, response_object: nil)
+ def configure_mock_auth(provider, uid, email, response_object: nil)
# The mock_auth configuration allows you to set per-provider (or default)
# authentication hashes to return during integration testing.
OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({
@@ -118,6 +123,11 @@ module LoginHelpers
response_object: response_object
}
})
+ end
+
+ def mock_auth_hash(provider, uid, email, response_object: nil)
+ configure_mock_auth(provider, uid, email, response_object: response_object)
+
original_env_config_omniauth_auth = Rails.application.env_config['omniauth.auth']
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
diff --git a/spec/support/helpers/position_tracer_helpers.rb b/spec/support/helpers/position_tracer_helpers.rb
new file mode 100644
index 00000000000..bbf6e06dd40
--- /dev/null
+++ b/spec/support/helpers/position_tracer_helpers.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module PositionTracerHelpers
+ def diff_refs(base_commit, head_commit)
+ Gitlab::Diff::DiffRefs.new(base_sha: base_commit.id, head_sha: head_commit.id)
+ end
+
+ def position(attrs = {})
+ attrs.reverse_merge!(
+ diff_refs: old_diff_refs
+ )
+ Gitlab::Diff::Position.new(attrs)
+ end
+
+ def expect_new_position(attrs, result = subject)
+ aggregate_failures("expect new position #{attrs.inspect}") do
+ if attrs.nil?
+ expect(result[:outdated]).to be_truthy
+ else
+ new_position = result[:position]
+
+ expect(result[:outdated]).to be_falsey
+ expect(new_position).not_to be_nil
+ expect(new_position.diff_refs).to eq(new_diff_refs)
+
+ attrs.each do |attr, value|
+ expect(new_position.send(attr)).to eq(value)
+ end
+ end
+ end
+ end
+
+ def expect_change_position(attrs, result = subject)
+ aggregate_failures("expect change position #{attrs.inspect}") do
+ change_position = result[:position]
+
+ expect(result[:outdated]).to be_truthy
+
+ if attrs.nil? || attrs.empty?
+ expect(change_position).to be_nil
+ else
+ expect(change_position).not_to be_nil
+ expect(change_position.diff_refs).to eq(change_diff_refs)
+
+ attrs.each do |attr, value|
+ expect(change_position.send(attr)).to eq(value)
+ end
+ end
+ end
+ end
+
+ def create_branch(new_name, branch_name)
+ CreateBranchService.new(project, current_user).execute(new_name, branch_name)
+ end
+
+ def create_file(branch_name, file_name, content)
+ Files::CreateService.new(
+ project,
+ current_user,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: "Create file",
+ file_path: file_name,
+ file_content: content
+ ).execute
+ project.commit(branch_name)
+ end
+
+ def update_file(branch_name, file_name, content)
+ Files::UpdateService.new(
+ project,
+ current_user,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: "Update file",
+ file_path: file_name,
+ file_content: content
+ ).execute
+ project.commit(branch_name)
+ end
+
+ def delete_file(branch_name, file_name)
+ Files::DeleteService.new(
+ project,
+ current_user,
+ start_branch: branch_name,
+ branch_name: branch_name,
+ commit_message: "Delete file",
+ file_path: file_name
+ ).execute
+ project.commit(branch_name)
+ end
+end
diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index b76b53db0b9..528da37e8cf 100644
--- a/spec/support/helpers/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
@@ -10,7 +10,7 @@ module ReactiveCachingHelpers
def stub_reactive_cache(subject = nil, data = nil, *qualifiers)
allow(ReactiveCachingWorker).to receive(:perform_async)
allow(ReactiveCachingWorker).to receive(:perform_in)
- write_reactive_cache(subject, data, *qualifiers) unless data.nil?
+ write_reactive_cache(subject, data, *qualifiers) unless subject.nil?
end
def synchronous_reactive_cache(subject)
diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb
index 45c6e04dbf3..6840fb9a860 100644
--- a/spec/support/test_reports/test_reports_helper.rb
+++ b/spec/support/test_reports/test_reports_helper.rb
@@ -1,36 +1,36 @@
module TestReportsHelper
- def create_test_case_rspec_success
+ def create_test_case_rspec_success(name = 'test_spec')
Gitlab::Ci::Reports::TestCase.new(
name: 'Test#sum when a is 1 and b is 3 returns summary',
- classname: 'spec.test_spec',
+ classname: "spec.#{name}",
file: './spec/test_spec.rb',
execution_time: 1.11,
status: Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
end
- def create_test_case_rspec_failed
+ def create_test_case_rspec_failed(name = 'test_spec')
Gitlab::Ci::Reports::TestCase.new(
- name: 'Test#sum when a is 2 and b is 2 returns summary',
- classname: 'spec.test_spec',
+ name: 'Test#sum when a is 1 and b is 3 returns summary',
+ classname: "spec.#{name}",
file: './spec/test_spec.rb',
execution_time: 2.22,
system_output: sample_rspec_failed_message,
status: Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
end
- def create_test_case_rspec_skipped
+ def create_test_case_rspec_skipped(name = 'test_spec')
Gitlab::Ci::Reports::TestCase.new(
name: 'Test#sum when a is 3 and b is 3 returns summary',
- classname: 'spec.test_spec',
+ classname: "spec.#{name}",
file: './spec/test_spec.rb',
execution_time: 3.33,
status: Gitlab::Ci::Reports::TestCase::STATUS_SKIPPED)
end
- def create_test_case_rspec_error
+ def create_test_case_rspec_error(name = 'test_spec')
Gitlab::Ci::Reports::TestCase.new(
name: 'Test#sum when a is 4 and b is 4 returns summary',
- classname: 'spec.test_spec',
+ classname: "spec.#{name}",
file: './spec/test_spec.rb',
execution_time: 4.44,
status: Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
@@ -48,34 +48,34 @@ module TestReportsHelper
EOF
end
- def create_test_case_java_success
+ def create_test_case_java_success(name = 'addTest')
Gitlab::Ci::Reports::TestCase.new(
- name: 'addTest',
+ name: name,
classname: 'CalculatorTest',
execution_time: 5.55,
status: Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS)
end
- def create_test_case_java_failed
+ def create_test_case_java_failed(name = 'addTest')
Gitlab::Ci::Reports::TestCase.new(
- name: 'subtractTest',
+ name: name,
classname: 'CalculatorTest',
execution_time: 6.66,
system_output: sample_java_failed_message,
status: Gitlab::Ci::Reports::TestCase::STATUS_FAILED)
end
- def create_test_case_java_skipped
+ def create_test_case_java_skipped(name = 'addTest')
Gitlab::Ci::Reports::TestCase.new(
- name: 'multiplyTest',
+ name: name,
classname: 'CalculatorTest',
execution_time: 7.77,
status: Gitlab::Ci::Reports::TestCase::STATUS_SKIPPED)
end
- def create_test_case_java_error
+ def create_test_case_java_error(name = 'addTest')
Gitlab::Ci::Reports::TestCase.new(
- name: 'divideTest',
+ name: name,
classname: 'CalculatorTest',
execution_time: 8.88,
status: Gitlab::Ci::Reports::TestCase::STATUS_ERROR)
diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb
index a9e03f3d4e5..5ee0a10f38d 100644
--- a/spec/uploaders/file_mover_spec.rb
+++ b/spec/uploaders/file_mover_spec.rb
@@ -85,8 +85,7 @@ describe FileMover do
context 'when tmp uploader is not local storage' do
before do
- allow(PersonalFileUploader).to receive(:object_store_enabled?) { true }
- tmp_uploader.object_store = ObjectStorage::Store::REMOTE
+ stub_uploads_object_storage(uploader: PersonalFileUploader)
allow_any_instance_of(PersonalFileUploader).to receive(:file_storage?) { false }
end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 185c62491ce..04206de3dc6 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -184,40 +184,37 @@ describe FileUploader do
end
end
- describe '#cache!' do
- subject do
- uploader.store!(uploaded_file)
- end
+ context 'when remote file is used' do
+ let(:temp_file) { Tempfile.new("test") }
- context 'when remote file is used' do
- let(:temp_file) { Tempfile.new("test") }
+ let!(:fog_connection) do
+ stub_uploads_object_storage(described_class)
+ end
- let!(:fog_connection) do
- stub_uploads_object_storage(described_class)
- end
+ let(:filename) { "my file.txt" }
+ let(:uploaded_file) do
+ UploadedFile.new(temp_file.path, filename: filename, remote_id: "test/123123")
+ end
- let(:uploaded_file) do
- UploadedFile.new(temp_file.path, filename: "my file.txt", remote_id: "test/123123")
- end
+ let!(:fog_file) do
+ fog_connection.directories.new(key: 'uploads').files.create(
+ key: 'tmp/uploads/test/123123',
+ body: 'content'
+ )
+ end
- let!(:fog_file) do
- fog_connection.directories.new(key: 'uploads').files.create(
- key: 'tmp/uploads/test/123123',
- body: 'content'
- )
- end
+ before do
+ FileUtils.touch(temp_file)
- before do
- FileUtils.touch(temp_file)
- end
+ uploader.store!(uploaded_file)
+ end
- after do
- FileUtils.rm_f(temp_file)
- end
+ after do
+ FileUtils.rm_f(temp_file)
+ end
+ describe '#cache!' do
it 'file is stored remotely in permament location with sanitized name' do
- subject
-
expect(uploader).to be_exists
expect(uploader).not_to be_cached
expect(uploader).not_to be_file_storage
@@ -228,5 +225,18 @@ describe FileUploader do
expect(uploader.object_store).to eq(described_class::Store::REMOTE)
end
end
+
+ describe '#to_h' do
+ subject { uploader.to_h }
+
+ let(:filename) { 'my+file.txt' }
+
+ it 'generates URL using original file name instead of filename returned by object storage' do
+ # GCS returns a URL with a `+` instead of `%2B`
+ allow(uploader.file).to receive(:url).and_return('https://storage.googleapis.com/gitlab-test-uploads/@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b/64c5065e62100b1a12841644256a98be/my+file.txt')
+
+ expect(subject[:url]).to end_with(filename)
+ end
+ end
end
end
diff --git a/spec/workers/namespaces/schedule_aggregation_worker_spec.rb b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
index 7432ca12f2a..d4a49a3f53a 100644
--- a/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
+++ b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Namespaces::ScheduleAggregationWorker, '#perform' do
+describe Namespaces::ScheduleAggregationWorker, '#perform', :clean_gitlab_redis_shared_state do
let(:group) { create(:group) }
subject(:worker) { described_class.new }
@@ -10,6 +10,8 @@ describe Namespaces::ScheduleAggregationWorker, '#perform' do
context 'when group is the root ancestor' do
context 'when aggregation schedule exists' do
it 'does not create a new one' do
+ stub_aggregation_schedule_statistics
+
Namespace::AggregationSchedule.safe_find_or_create_by!(namespace_id: group.id)
expect do
@@ -18,26 +20,25 @@ describe Namespaces::ScheduleAggregationWorker, '#perform' do
end
end
- context 'when update_statistics_namespace is off' do
- it 'does not create a new one' do
- stub_feature_flags(update_statistics_namespace: false, namespace: group)
+ context 'when aggregation schedule does not exist' do
+ it 'creates one' do
+ stub_aggregation_schedule_statistics
expect do
worker.perform(group.id)
- end.not_to change(Namespace::AggregationSchedule, :count)
+ end.to change(Namespace::AggregationSchedule, :count).by(1)
+
+ expect(group.aggregation_schedule).to be_present
end
end
- context 'when aggregation schedule does not exist' do
- it 'creates one' do
- allow_any_instance_of(Namespace::AggregationSchedule)
- .to receive(:schedule_root_storage_statistics).and_return(nil)
+ context 'when update_statistics_namespace is off' do
+ it 'does not create a new one' do
+ stub_feature_flags(update_statistics_namespace: false, namespace: group)
expect do
worker.perform(group.id)
- end.to change(Namespace::AggregationSchedule, :count).by(1)
-
- expect(group.aggregation_schedule).to be_present
+ end.not_to change(Namespace::AggregationSchedule, :count)
end
end
end
@@ -47,8 +48,7 @@ describe Namespaces::ScheduleAggregationWorker, '#perform' do
let(:group) { create(:group, parent: parent_group) }
it 'creates an aggregation schedule for the root' do
- allow_any_instance_of(Namespace::AggregationSchedule)
- .to receive(:schedule_root_storage_statistics).and_return(nil)
+ stub_aggregation_schedule_statistics
worker.perform(group.id)
@@ -63,4 +63,15 @@ describe Namespaces::ScheduleAggregationWorker, '#perform' do
worker.perform(12345)
end
end
+
+ def stub_aggregation_schedule_statistics
+ # Namespace::Aggregations are deleted by
+ # Namespace::AggregationSchedule::schedule_root_storage_statistics,
+ # which is executed async. Stubing the service so instances are not deleted
+ # while still running the specs.
+ expect_next_instance_of(Namespace::AggregationSchedule) do |aggregation_schedule|
+ expect(aggregation_schedule)
+ .to receive(:schedule_root_storage_statistics)
+ end
+ end
end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
index 51afb076da1..edc55920b8e 100644
--- a/spec/workers/project_cache_worker_spec.rb
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -36,6 +36,11 @@ describe ProjectCacheWorker do
end
context 'with an existing project' do
+ before do
+ lease_key = "namespace:namespaces_root_statistics:#{project.namespace_id}"
+ stub_exclusive_lease_taken(lease_key, timeout: Namespace::AggregationSchedule::DEFAULT_LEASE_TIMEOUT)
+ end
+
it 'refreshes the method caches' do
expect_any_instance_of(Repository).to receive(:refresh_method_caches)
.with(%i(readme))
@@ -81,6 +86,10 @@ describe ProjectCacheWorker do
expect(UpdateProjectStatisticsWorker).not_to receive(:perform_in)
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+ .with(project.namespace_id)
+
worker.update_statistics(project, statistics)
end
end
@@ -98,6 +107,11 @@ describe ProjectCacheWorker do
.with(lease_timeout, project.id, statistics)
.and_call_original
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async)
+ .with(project.namespace_id)
+ .twice
+
worker.update_statistics(project, statistics)
end
end
diff --git a/yarn.lock b/yarn.lock
index 1e04c82df1c..dc5e0662396 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -700,15 +700,15 @@
dependencies:
requireindex "~1.1.0"
-"@gitlab/svgs@^1.66.0":
- version "1.66.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.66.0.tgz#3c02da455421ea241f32e915671842435df027ff"
- integrity sha512-nxOoQPnofMs3BjRr3SVzQcclM0G6QFrLM8L4nnUCN+8Gxq2u8ukfSU5FCrkivXz+FP9Qo/FYilWV7CY8kDkt6A==
+"@gitlab/svgs@^1.67.0":
+ version "1.67.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579"
+ integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA==
-"@gitlab/ui@^5.1.0":
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.1.0.tgz#b8c8f266edc68f616e92c0ba3a18a692002393e4"
- integrity sha512-IPgk5W7mSXcbni+zNuJeVU89Co72jSQAXTxU7AtmItt5XT6nI9US2ZAWNUl8XCiOOw81jzYv0PLp4bMiXdLkww==
+"@gitlab/ui@^5.5.0":
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.5.0.tgz#2000b2ed0c3825dd8c4430191023f4d03c923ecb"
+ integrity sha512-6e/AFFLDk/gm4wKnHM9rcpTRqCsWkPKW/Vjsnd6h4wyfif2/TusHeIn/jedQxUaORbO/XZKzg4V5COhXXbCx4w==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1"
@@ -7476,9 +7476,9 @@ mississippi@^3.0.0:
through2 "^2.0.0"
mixin-deep@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
- integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+ integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
dependencies:
for-in "^1.0.2"
is-extendable "^1.0.1"