summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Amirault <mamirault@gitlab.com>2019-07-10 05:56:23 +0000
committerMarcel Amirault <mamirault@gitlab.com>2019-07-10 05:56:23 +0000
commit2b03a0c2cf699074ec37a863e5c752a2cab2b133 (patch)
tree7ebc289d2ec498fcebf8ae454cdb1eb1ef500dbd
parentc0deda7a86796c5de6ebe376c83f55af0965bde3 (diff)
parent810df4fb51bf3db4016c5f7458599331d4586300 (diff)
downloadgitlab-ce-docs-hard-tabs.tar.gz
Merge branch 'master' into 'docs-hard-tabs'docs-hard-tabs
# Conflicts: # doc/administration/pseudonymizer.md # doc/administration/uploads.md
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml4
-rw-r--r--.mdlrc4
-rw-r--r--.mdlrc.style7
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock18
-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/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/project_form_group.vue8
-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/notes/components/comment_form.vue11
-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/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/sidebar_assignees.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue3
-rw-r--r--app/assets/javascripts/vue_shared/directives/tooltip.js4
-rw-r--r--app/assets/stylesheets/components/toast.scss13
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss4
-rw-r--r--app/assets/stylesheets/framework/modal.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/diff.scss4
-rw-r--r--app/assets/stylesheets/pages/notes.scss1
-rw-r--r--app/assets/stylesheets/pages/wiki.scss4
-rw-r--r--app/controllers/concerns/issuable_actions.rb4
-rw-r--r--app/controllers/graphql_controller.rb5
-rw-r--r--app/controllers/projects/issues_controller.rb6
-rw-r--r--app/graphql/gitlab_schema.rb2
-rw-r--r--app/graphql/mutations/notes/base.rb34
-rw-r--r--app/graphql/mutations/notes/create/base.rb49
-rw-r--r--app/graphql/mutations/notes/create/diff_note.rb33
-rw-r--r--app/graphql/mutations/notes/create/image_diff_note.rb33
-rw-r--r--app/graphql/mutations/notes/create/note.rb40
-rw-r--r--app/graphql/mutations/notes/destroy.rb28
-rw-r--r--app/graphql/mutations/notes/update.rb38
-rw-r--r--app/graphql/types/base_input_object.rb1
-rw-r--r--app/graphql/types/diff_paths_input_type.rb12
-rw-r--r--app/graphql/types/diff_refs_type.rb14
-rw-r--r--app/graphql/types/merge_request_type.rb1
-rw-r--r--app/graphql/types/mutation_type.rb5
-rw-r--r--app/graphql/types/notes/diff_image_position_input_type.rb20
-rw-r--r--app/graphql/types/notes/diff_position_base_input_type.rb22
-rw-r--r--app/graphql/types/notes/diff_position_input_type.rb16
-rw-r--r--app/graphql/types/notes/diff_position_type.rb7
-rw-r--r--app/graphql/types/notes/discussion_type.rb8
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/storage_helper.rb2
-rw-r--r--app/models/concerns/project_api_compatibility.rb20
-rw-r--r--app/models/concerns/project_features_compatibility.rb54
-rw-r--r--app/models/concerns/reactive_caching.rb6
-rw-r--r--app/models/discussion.rb11
-rw-r--r--app/models/label_note.rb18
-rw-r--r--app/models/merge_request.rb13
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb3
-rw-r--r--app/models/project_feature.rb18
-rw-r--r--app/models/project_statistics.rb25
-rw-r--r--app/models/repository.rb4
-rw-r--r--app/models/resource_label_event.rb7
-rw-r--r--app/services/ci/register_job_service.rb2
-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/merge_base_service.rb11
-rw-r--r--app/services/merge_requests/merge_service.rb4
-rw-r--r--app/services/merge_requests/merge_to_ref_service.rb18
-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/groups/issues.html.haml9
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/shared/_storage_counter_statistics.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml3
-rw-r--r--changelogs/unreleased/61284-frontend-follow-up-from-add-packages_size-to-projectstatistics.yml5
-rw-r--r--changelogs/unreleased/62826-graphql-note-mutations.yml5
-rw-r--r--changelogs/unreleased/63945-update-mixin-deep-to-1-3-2.yml5
-rw-r--r--changelogs/unreleased/64066-fix-uneven-click-areas.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/fix-median-counting-for-cycle-analytics.yml5
-rw-r--r--changelogs/unreleased/fix-unicorn-sampler-workers-count.yml5
-rw-r--r--changelogs/unreleased/fj-fix-subgroup-search-url.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/patch-29.yml5
-rw-r--r--changelogs/unreleased/project_api.yml5
-rw-r--r--changelogs/unreleased/tz-update-mr-count-over-tabs.yml6
-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/geo.rb17
-rw-r--r--config/initializers/health_check.rb6
-rw-r--r--config/initializers/load_balancing.rb25
-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/routes/api.rb6
-rw-r--r--danger/only_documentation/Dangerfile2
-rw-r--r--doc/administration/audit_events.md2
-rw-r--r--doc/administration/auditor_users.md2
-rw-r--r--doc/administration/auth/smartcard.md28
-rw-r--r--doc/administration/container_registry.md300
-rw-r--r--doc/administration/database_load_balancing.md26
-rw-r--r--doc/administration/geo/replication/index.md2
-rw-r--r--doc/administration/geo/replication/security_review.md2
-rw-r--r--doc/administration/gitaly/index.md8
-rw-r--r--doc/administration/high_availability/consul.md66
-rw-r--r--doc/administration/high_availability/database.md59
-rw-r--r--doc/administration/high_availability/gitlab.md2
-rw-r--r--doc/administration/high_availability/redis.md6
-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.md40
-rw-r--r--doc/administration/index.md2
-rw-r--r--doc/administration/issue_closing_pattern.md13
-rw-r--r--doc/administration/job_artifacts.md154
-rw-r--r--doc/administration/job_traces.md48
-rw-r--r--doc/administration/merge_request_diffs.md116
-rw-r--r--doc/administration/packages.md142
-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/api/README.md14
-rw-r--r--doc/api/boards.md1
-rw-r--r--doc/api/deploy_keys.md4
-rw-r--r--doc/api/epics.md2
-rw-r--r--doc/api/graphql/index.md16
-rw-r--r--doc/api/issue_links.md3
-rw-r--r--doc/api/issues.md6
-rw-r--r--doc/api/issues_statistics.md7
-rw-r--r--doc/api/jobs.md3
-rw-r--r--doc/api/license.md1
-rw-r--r--doc/api/lint.md38
-rw-r--r--doc/api/managed_licenses.md2
-rw-r--r--doc/api/merge_request_approvals.md2
-rw-r--r--doc/api/oauth2.md2
-rw-r--r--doc/api/packages.md2
-rw-r--r--doc/api/project_badges.md2
-rw-r--r--doc/api/project_statistics.md2
-rw-r--r--doc/api/project_templates.md4
-rw-r--r--doc/api/projects.md123
-rw-r--r--doc/api/repositories.md6
-rw-r--r--doc/api/runners.md8
-rw-r--r--doc/api/scim.md2
-rw-r--r--doc/api/users.md25
-rw-r--r--doc/ci/docker/using_docker_images.md10
-rw-r--r--doc/ci/git_submodules.md16
-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/customization/system_header_and_footer_messages.md4
-rw-r--r--doc/development/api_styleguide.md2
-rw-r--r--doc/development/architecture.md6
-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.md9
-rw-r--r--doc/development/contributing/style_guides.md8
-rw-r--r--doc/development/documentation/styleguide.md4
-rw-r--r--doc/development/ee_features.md70
-rw-r--r--doc/development/emails.md96
-rw-r--r--doc/development/fe_guide/architecture.md2
-rw-r--r--doc/development/fe_guide/emojis.md4
-rw-r--r--doc/development/fe_guide/graphql.md4
-rw-r--r--doc/development/gotchas.md8
-rw-r--r--doc/development/integrations/jira_connect.md4
-rw-r--r--doc/development/licensed_feature_availability.md20
-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.md38
-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/routing.md34
-rw-r--r--doc/development/sql.md4
-rw-r--r--doc/development/testing_guide/index.md3
-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/install/google_cloud_platform/index.md2
-rw-r--r--doc/install/openshift_and_gitlab/index.md17
-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/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.md2
-rw-r--r--doc/policy/maintenance.md2
-rw-r--r--doc/raketasks/README.md2
-rw-r--r--doc/topics/autodevops/index.md11
-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/upgrading_from_source.md2
-rw-r--r--doc/user/admin_area/custom_project_templates.md2
-rw-r--r--doc/user/admin_area/settings/external_authorization.md2
-rw-r--r--doc/user/admin_area/settings/instance_template_repository.md2
-rw-r--r--doc/user/asciidoc.md2
-rw-r--r--doc/user/discussions/index.md2
-rw-r--r--doc/user/gitlab_com/index.md4
-rw-r--r--doc/user/group/custom_project_templates.md2
-rw-r--r--doc/user/group/roadmap/index.md4
-rw-r--r--doc/user/project/clusters/index.md3
-rw-r--r--doc/user/project/deploy_tokens/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/integrations/github.md2
-rw-r--r--doc/user/project/integrations/prometheus.md9
-rw-r--r--doc/user/project/integrations/prometheus_library/cloudwatch.md2
-rw-r--r--doc/user/project/integrations/slack.md2
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md2
-rw-r--r--doc/user/project/operations/tracing.md8
-rw-r--r--doc/user/project/packages/npm_registry.md15
-rw-r--r--doc/user/project/settings/import_export.md7
-rw-r--r--doc/workflow/lfs/lfs_administration.md2
-rw-r--r--doc/workflow/timezone.md8
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb28
-rw-r--r--lib/api/helpers/graphql_helpers.rb2
-rw-r--r--lib/api/helpers/projects_helpers.rb45
-rw-r--r--lib/api/import_github.rb2
-rw-r--r--lib/api/user_counts.rb18
-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/database/median.rb2
-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.rb41
-rw-r--r--lib/gitlab/global_id.rb14
-rw-r--r--lib/gitlab/gon_helper.rb5
-rw-r--r--lib/gitlab/graphql.rb4
-rw-r--r--lib/gitlab/metrics/samplers/unicorn_sampler.rb11
-rw-r--r--lib/peek/views/redis.rb8
-rw-r--r--locale/gitlab.pot10
-rw-r--r--package.json2
-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/runtime/browser.rb6
-rw-r--r--qa/qa/runtime/env.rb12
-rw-r--r--qa/qa/runtime/scenario.rb6
-rw-r--r--qa/qa/scenario/shared_attributes.rb1
-rw-r--r--qa/qa/service/kubernetes_cluster.rb14
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb2
-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/trigger-build-docs4
-rw-r--r--spec/features/admin/admin_sees_project_statistics_spec.rb2
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb124
-rw-r--r--spec/frontend/api_spec.js16
-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.snap38
-rw-r--r--spec/frontend/notes/components/discussion_notes_replies_wrapper_spec.js51
-rw-r--r--spec/graphql/gitlab_schema_spec.rb20
-rw-r--r--spec/graphql/types/diff_refs_type_spec.rb9
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb2
-rw-r--r--spec/graphql/types/notes/diff_position_type_spec.rb2
-rw-r--r--spec/graphql/types/notes/discussion_type_spec.rb2
-rw-r--r--spec/helpers/storage_helper_spec.rb2
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js3
-rw-r--r--spec/javascripts/issuable_spec.js12
-rw-r--r--spec/javascripts/notes/components/comment_form_spec.js15
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js3
-rw-r--r--spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb1
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_stage_spec.rb34
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb2
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb99
-rw-r--r--spec/lib/gitlab/global_id_spec.rb37
-rw-r--r--spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb33
-rw-r--r--spec/lib/peek/views/redis_detailed_spec.rb36
-rw-r--r--spec/models/concerns/project_api_compatibility_spec.rb38
-rw-r--r--spec/models/concerns/project_features_compatibility_spec.rb18
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb56
-rw-r--r--spec/models/discussion_spec.rb14
-rw-r--r--spec/models/merge_request_spec.rb30
-rw-r--r--spec/models/project_statistics_spec.rb43
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb64
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb70
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb64
-rw-r--r--spec/requests/api/graphql/mutations/notes/destroy_spec.rb52
-rw-r--r--spec/requests/api/graphql/mutations/notes/update_spec.rb72
-rw-r--r--spec/requests/api/graphql_spec.rb10
-rw-r--r--spec/requests/api/projects_spec.rb54
-rw-r--r--spec/requests/api/user_counts_spec.rb40
-rw-r--r--spec/routing/api_routing_spec.rb23
-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_service_spec.rb13
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb22
-rw-r--r--spec/support/helpers/git_http_helpers.rb4
-rw-r--r--spec/support/helpers/graphql_helpers.rb15
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb2
-rw-r--r--spec/support/shared_examples/graphql/notes_creation_shared_examples.rb61
-rw-r--r--spec/workers/project_cache_worker_spec.rb14
-rw-r--r--yarn.lock14
323 files changed, 4485 insertions, 1962 deletions
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index 57ee91e4385..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 \
- MD001,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/.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/Gemfile b/Gemfile
index f65549f2734..f32e899342b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -309,7 +309,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
- gem 'prometheus-client-mmap', '~> 0.9.7'
+ 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
diff --git a/Gemfile.lock b/Gemfile.lock
index c1deaa74e1f..85b4c32f168 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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.7)
+ prometheus-client-mmap (0.9.8)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
@@ -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)
@@ -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.7)
+ prometheus-client-mmap (~> 0.9.8)
pry-byebug (~> 3.5.1)
pry-rails (~> 0.3.4)
puma (~> 3.12)
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/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/project_form_group.vue b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
index b89729375be..99d77a75c23 100644
--- a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
+++ b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
@@ -41,7 +41,7 @@ export default {
noForkText() {
return sprintf(
__(
- 'To protect this issues confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.',
+ "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,
@@ -118,7 +118,7 @@ export default {
<template v-if="projects.length">
{{
__(
- 'To protect this issues confidentiality, a private fork of this project was selected.',
+ "To protect this issue's confidentiality, a private fork of this project was selected.",
)
}}
</template>
@@ -126,10 +126,6 @@ export default {
{{ __('No forks available to you.') }}<br />
<span v-html="noForkText"></span>
</template>
- <gl-link :href="helpPagePath" class="help-link" target="_blank">
- <span class="sr-only">{{ __('Read more') }}</span>
- <i class="fa fa-question-circle" aria-hidden="true"></i>
- </gl-link>
</p>
</div>
</div>
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/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 6c1738f0f1b..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';
@@ -234,7 +235,10 @@ export default {
toggleIssueState() {
if (this.isOpen) {
this.closeIssue()
- .then(() => this.enableButton())
+ .then(() => {
+ this.enableButton();
+ refreshUserMergeRequestCounts();
+ })
.catch(() => {
this.enableButton();
this.toggleStateButtonLoading(false);
@@ -247,7 +251,10 @@ export default {
});
} else {
this.reopenIssue()
- .then(() => this.enableButton())
+ .then(() => {
+ this.enableButton();
+ refreshUserMergeRequestCounts();
+ })
.catch(({ data }) => {
this.enableButton();
this.toggleStateButtonLoading(false);
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/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/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index 70dc3d2cdfa..be1e4811856 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -2,6 +2,7 @@
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';
@@ -73,6 +74,9 @@ export default {
this.mediator
.saveAssignees(this.field)
.then(setLoadingFalse.bind(this))
+ .then(() => {
+ refreshUserMergeRequestCounts();
+ })
.catch(() => {
setLoadingFalse();
return new Flash(__('Error occurred when saving assignees'));
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 d1f75593d14..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) {
diff --git a/app/assets/javascripts/vue_shared/directives/tooltip.js b/app/assets/javascripts/vue_shared/directives/tooltip.js
index 2d1f7a1cfd0..73e92728cb9 100644
--- a/app/assets/javascripts/vue_shared/directives/tooltip.js
+++ b/app/assets/javascripts/vue_shared/directives/tooltip.js
@@ -3,8 +3,12 @@ import '~/commons/bootstrap';
export default {
bind(el) {
+ const glTooltipDelay = localStorage.getItem('gl-tooltip-delay');
+ const delay = glTooltipDelay ? JSON.parse(glTooltipDelay) : 0;
+
$(el).tooltip({
trigger: 'hover',
+ delay,
});
},
diff --git a/app/assets/stylesheets/components/toast.scss b/app/assets/stylesheets/components/toast.scss
index acbd909d595..e27bf282247 100644
--- a/app/assets/stylesheets/components/toast.scss
+++ b/app/assets/stylesheets/components/toast.scss
@@ -15,11 +15,15 @@
.toasted.gl-toast {
border-radius: $border-radius-default;
font-size: $gl-font-size;
- padding: $gl-padding-8 $gl-padding-24;
+ padding: $gl-padding-8 $gl-padding $gl-padding-8 $gl-padding-24;
margin-top: $toast-default-margin;
line-height: $gl-line-height;
background-color: rgba($gray-900, $toast-background-opacity);
+ span {
+ padding-right: $gl-padding-8;
+ }
+
@include media-breakpoint-down(xs) {
.action:first-of-type {
// Ensures actions buttons are right aligned on mobile
@@ -29,19 +33,14 @@
.action {
color: $blue-300;
- margin: 0 0 0 $toast-action-margin-left;
+ margin: 0 0 0 $toast-default-margin;
text-transform: none;
font-size: $gl-font-size;
-
- &:first-of-type {
- padding-right: 0;
- }
}
.toast-close {
font-size: $default-icon-size;
margin-left: $toast-default-margin;
- padding-left: $gl-padding;
}
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index a12029d2419..e75c1379dfb 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -287,8 +287,8 @@
list-style: none;
padding: 0 1px;
- a:not(.help-link),
- button:not(.btn),
+ a,
+ 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..4521643ce08 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -507,7 +507,6 @@ $toast-height: 48px;
$toast-max-width: 586px;
$toast-padding-right: 42px;
$toast-default-margin: 8px;
-$toast-action-margin-left: 16px;
$toast-background-opacity: 0.95;
/*
@@ -805,7 +804,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 5db0136e3f1..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;
}
}
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/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/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index b866f574f67..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
@@ -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/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index 152ebb930e2..7edd14e48f7 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -66,6 +66,8 @@ class GitlabSchema < GraphQL::Schema
if gid.model_class < ApplicationRecord
Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
+ elsif gid.model_class.respond_to?(:lazy_find)
+ gid.model_class.lazy_find(gid.model_id)
else
gid.find
end
diff --git a/app/graphql/mutations/notes/base.rb b/app/graphql/mutations/notes/base.rb
new file mode 100644
index 00000000000..a7198f5fba6
--- /dev/null
+++ b/app/graphql/mutations/notes/base.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ class Base < BaseMutation
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ field :note,
+ Types::Notes::NoteType,
+ null: true,
+ description: 'The note after mutation'
+
+ private
+
+ def find_object(id:)
+ GitlabSchema.object_from_id(id)
+ end
+
+ def check_object_is_noteable!(object)
+ unless object.is_a?(Noteable)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable,
+ 'Cannot add notes to this resource'
+ end
+ end
+
+ def check_object_is_note!(object)
+ unless object.is_a?(Note)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable,
+ 'Resource is not a note'
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb
new file mode 100644
index 00000000000..d3a5dae2188
--- /dev/null
+++ b/app/graphql/mutations/notes/create/base.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ module Create
+ # This is a Base class for the Note creation Mutations and is not
+ # mounted as a GraphQL mutation itself.
+ class Base < Mutations::Notes::Base
+ authorize :create_note
+
+ argument :noteable_id,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The global id of the resource to add a note to'
+
+ argument :body,
+ GraphQL::STRING_TYPE,
+ required: true,
+ description: copy_field_description(Types::Notes::NoteType, :body)
+
+ private
+
+ def resolve(args)
+ noteable = authorized_find!(id: args[:noteable_id])
+
+ check_object_is_noteable!(noteable)
+
+ note = ::Notes::CreateService.new(
+ noteable.project,
+ current_user,
+ create_note_params(noteable, args)
+ ).execute
+
+ {
+ note: (note if note.persisted?),
+ errors: errors_on_object(note)
+ }
+ end
+
+ def create_note_params(noteable, args)
+ {
+ noteable: noteable,
+ note: args[:body]
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/diff_note.rb b/app/graphql/mutations/notes/create/diff_note.rb
new file mode 100644
index 00000000000..9b5f3092006
--- /dev/null
+++ b/app/graphql/mutations/notes/create/diff_note.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ module Create
+ class DiffNote < Base
+ graphql_name 'CreateDiffNote'
+
+ argument :position,
+ Types::Notes::DiffPositionInputType,
+ required: true,
+ description: copy_field_description(Types::Notes::NoteType, :position)
+
+ private
+
+ def create_note_params(noteable, args)
+ super(noteable, args).merge({
+ type: 'DiffNote',
+ position: position(noteable, args)
+ })
+ end
+
+ def position(noteable, args)
+ position = args[:position].to_h
+ position[:position_type] = 'text'
+ position.merge!(position[:paths].to_h)
+
+ Gitlab::Diff::Position.new(position)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/image_diff_note.rb b/app/graphql/mutations/notes/create/image_diff_note.rb
new file mode 100644
index 00000000000..d94fd4d6ff8
--- /dev/null
+++ b/app/graphql/mutations/notes/create/image_diff_note.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ module Create
+ class ImageDiffNote < Base
+ graphql_name 'CreateImageDiffNote'
+
+ argument :position,
+ Types::Notes::DiffImagePositionInputType,
+ required: true,
+ description: copy_field_description(Types::Notes::NoteType, :position)
+
+ private
+
+ def create_note_params(noteable, args)
+ super(noteable, args).merge({
+ type: 'DiffNote',
+ position: position(noteable, args)
+ })
+ end
+
+ def position(noteable, args)
+ position = args[:position].to_h
+ position[:position_type] = 'image'
+ position.merge!(position[:paths].to_h)
+
+ Gitlab::Diff::Position.new(position)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
new file mode 100644
index 00000000000..5236e48026e
--- /dev/null
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ module Create
+ class Note < Base
+ graphql_name 'CreateNote'
+
+ argument :discussion_id,
+ GraphQL::ID_TYPE,
+ required: false,
+ description: 'The global id of the discussion this note is in reply to'
+
+ private
+
+ def create_note_params(noteable, args)
+ discussion_id = nil
+
+ if args[:discussion_id]
+ discussion = GitlabSchema.object_from_id(args[:discussion_id])
+ authorize_discussion!(discussion)
+
+ discussion_id = discussion.id
+ end
+
+ super(noteable, args).merge({
+ in_reply_to_discussion_id: discussion_id
+ })
+ end
+
+ def authorize_discussion!(discussion)
+ unless Ability.allowed?(current_user, :read_note, discussion, scope: :user)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable,
+ "The discussion does not exist or you don't have permission to perform this action"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb
new file mode 100644
index 00000000000..a81322bc9b7
--- /dev/null
+++ b/app/graphql/mutations/notes/destroy.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ class Destroy < Base
+ graphql_name 'DestroyNote'
+
+ authorize :admin_note
+
+ argument :id,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The global id of the note to destroy'
+
+ def resolve(id:)
+ note = authorized_find!(id: id)
+
+ check_object_is_note!(note)
+
+ ::Notes::DestroyService.new(note.project, current_user).execute(note)
+
+ {
+ errors: []
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/update.rb b/app/graphql/mutations/notes/update.rb
new file mode 100644
index 00000000000..ebf57b800c0
--- /dev/null
+++ b/app/graphql/mutations/notes/update.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ class Update < Base
+ graphql_name 'UpdateNote'
+
+ authorize :admin_note
+
+ argument :id,
+ GraphQL::ID_TYPE,
+ required: true,
+ description: 'The global id of the note to update'
+
+ argument :body,
+ GraphQL::STRING_TYPE,
+ required: true,
+ description: copy_field_description(Types::Notes::NoteType, :body)
+
+ def resolve(args)
+ note = authorized_find!(id: args[:id])
+
+ check_object_is_note!(note)
+
+ note = ::Notes::UpdateService.new(
+ note.project,
+ current_user,
+ { note: args[:body] }
+ ).execute(note)
+
+ {
+ note: note.reset,
+ errors: errors_on_object(note)
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb
index aebed035d3b..90a29b0cfb8 100644
--- a/app/graphql/types/base_input_object.rb
+++ b/app/graphql/types/base_input_object.rb
@@ -2,5 +2,6 @@
module Types
class BaseInputObject < GraphQL::Schema::InputObject
+ prepend Gitlab::Graphql::CopyFieldDescription
end
end
diff --git a/app/graphql/types/diff_paths_input_type.rb b/app/graphql/types/diff_paths_input_type.rb
new file mode 100644
index 00000000000..43feddd9827
--- /dev/null
+++ b/app/graphql/types/diff_paths_input_type.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class DiffPathsInputType < BaseInputObject
+ argument :old_path, GraphQL::STRING_TYPE, required: false,
+ description: 'The path of the file on the start sha'
+ argument :new_path, GraphQL::STRING_TYPE, required: false,
+ description: 'The path of the file on the head sha'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/diff_refs_type.rb b/app/graphql/types/diff_refs_type.rb
new file mode 100644
index 00000000000..33a5780cd68
--- /dev/null
+++ b/app/graphql/types/diff_refs_type.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ # Types that use DiffRefsType should have their own authorization
+ class DiffRefsType < BaseObject
+ graphql_name 'DiffRefs'
+
+ field :head_sha, GraphQL::STRING_TYPE, null: false, description: 'The sha of the head at the time the comment was made'
+ field :base_sha, GraphQL::STRING_TYPE, null: false, description: 'The merge base of the branch the comment was made on'
+ field :start_sha, GraphQL::STRING_TYPE, null: false, description: 'The sha of the branch being compared against'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 6734d4761c2..b8f63a750c5 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -23,6 +23,7 @@ module Types
field :updated_at, Types::TimeType, null: false
field :source_project, Types::ProjectType, null: true
field :target_project, Types::ProjectType, null: false
+ field :diff_refs, Types::DiffRefsType, null: true
# Alias for target_project
field :project, Types::ProjectType, null: false
field :project_id, GraphQL::INT_TYPE, null: false, method: :target_project_id
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index bc5fb709522..f843d6ad86f 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -10,5 +10,10 @@ module Types
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
mount_mutation Mutations::MergeRequests::SetWip, calls_gitaly: true
+ mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true
+ mount_mutation Mutations::Notes::Create::DiffNote, calls_gitaly: true
+ mount_mutation Mutations::Notes::Create::ImageDiffNote, calls_gitaly: true
+ mount_mutation Mutations::Notes::Update
+ mount_mutation Mutations::Notes::Destroy
end
end
diff --git a/app/graphql/types/notes/diff_image_position_input_type.rb b/app/graphql/types/notes/diff_image_position_input_type.rb
new file mode 100644
index 00000000000..23b53b20815
--- /dev/null
+++ b/app/graphql/types/notes/diff_image_position_input_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Notes
+ # rubocop: disable Graphql/AuthorizeTypes
+ class DiffImagePositionInputType < DiffPositionBaseInputType
+ graphql_name 'DiffImagePositionInput'
+
+ argument :x, GraphQL::INT_TYPE, required: true,
+ description: copy_field_description(Types::Notes::DiffPositionType, :x)
+ argument :y, GraphQL::INT_TYPE, required: true,
+ description: copy_field_description(Types::Notes::DiffPositionType, :y)
+ argument :width, GraphQL::INT_TYPE, required: true,
+ description: copy_field_description(Types::Notes::DiffPositionType, :width)
+ argument :height, GraphQL::INT_TYPE, required: true,
+ description: copy_field_description(Types::Notes::DiffPositionType, :height)
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/notes/diff_position_base_input_type.rb b/app/graphql/types/notes/diff_position_base_input_type.rb
new file mode 100644
index 00000000000..a9b4e1a8948
--- /dev/null
+++ b/app/graphql/types/notes/diff_position_base_input_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Notes
+ # rubocop: disable Graphql/AuthorizeTypes
+ class DiffPositionBaseInputType < BaseInputObject
+ argument :head_sha, GraphQL::STRING_TYPE, required: true,
+ description: copy_field_description(Types::DiffRefsType, :head_sha)
+ argument :base_sha, GraphQL::STRING_TYPE, required: false,
+ description: copy_field_description(Types::DiffRefsType, :base_sha)
+ argument :start_sha, GraphQL::STRING_TYPE, required: true,
+ description: copy_field_description(Types::DiffRefsType, :start_sha)
+
+ argument :paths,
+ Types::DiffPathsInputType,
+ required: true,
+ description: 'The paths of the file that was changed. ' \
+ 'Both of the properties of this input are optional, but at least one of them is required'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/notes/diff_position_input_type.rb b/app/graphql/types/notes/diff_position_input_type.rb
new file mode 100644
index 00000000000..02c91e173cb
--- /dev/null
+++ b/app/graphql/types/notes/diff_position_input_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ module Notes
+ # rubocop: disable Graphql/AuthorizeTypes
+ class DiffPositionInputType < DiffPositionBaseInputType
+ graphql_name 'DiffPositionInput'
+
+ argument :old_line, GraphQL::INT_TYPE, required: false,
+ description: copy_field_description(Types::Notes::DiffPositionType, :old_line)
+ argument :new_line, GraphQL::INT_TYPE, required: true,
+ description: copy_field_description(Types::Notes::DiffPositionType, :new_line)
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/notes/diff_position_type.rb b/app/graphql/types/notes/diff_position_type.rb
index ebc24451715..6a0377fbfdf 100644
--- a/app/graphql/types/notes/diff_position_type.rb
+++ b/app/graphql/types/notes/diff_position_type.rb
@@ -7,12 +7,7 @@ module Types
class DiffPositionType < BaseObject
graphql_name 'DiffPosition'
- field :head_sha, GraphQL::STRING_TYPE, null: false,
- description: "The sha of the head at the time the comment was made"
- field :base_sha, GraphQL::STRING_TYPE, null: true,
- description: "The merge base of the branch the comment was made on"
- field :start_sha, GraphQL::STRING_TYPE, null: false,
- description: "The sha of the branch being compared against"
+ field :diff_refs, Types::DiffRefsType, null: false
field :file_path, GraphQL::STRING_TYPE, null: false,
description: "The path of the file that was changed"
diff --git a/app/graphql/types/notes/discussion_type.rb b/app/graphql/types/notes/discussion_type.rb
index c4691942f2d..a3fb28298f6 100644
--- a/app/graphql/types/notes/discussion_type.rb
+++ b/app/graphql/types/notes/discussion_type.rb
@@ -8,8 +8,16 @@ module Types
authorize :read_note
field :id, GraphQL::ID_TYPE, null: false
+ field :reply_id, GraphQL::ID_TYPE, null: false, description: 'The ID used to reply to this discussion'
field :created_at, Types::TimeType, null: false
field :notes, Types::Notes::NoteType.connection_type, null: false, description: "All notes in the discussion"
+
+ # The gem we use to generate Global IDs is hard-coded to work with
+ # `id` properties. To generate a GID for the `reply_id` property,
+ # we must use the ::Gitlab::GlobalId module.
+ def reply_id
+ ::Gitlab::GlobalId.build(object, id: object.reply_id)
+ end
end
end
end
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/concerns/project_api_compatibility.rb b/app/models/concerns/project_api_compatibility.rb
new file mode 100644
index 00000000000..cb00efb06df
--- /dev/null
+++ b/app/models/concerns/project_api_compatibility.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# Add methods used by the projects API
+module ProjectAPICompatibility
+ extend ActiveSupport::Concern
+
+ def build_git_strategy=(value)
+ write_attribute(:build_allow_git_fetch, value == 'fetch')
+ end
+
+ def auto_devops_enabled=(value)
+ self.build_auto_devops if self.auto_devops&.enabled.nil?
+ self.auto_devops.update! enabled: value
+ end
+
+ def auto_devops_deploy_strategy=(value)
+ self.build_auto_devops if self.auto_devops&.enabled.nil?
+ self.auto_devops.update! deploy_strategy: value
+ end
+end
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index f268a842db4..551a2e56ecf 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -9,32 +9,70 @@ require 'gitlab/utils'
module ProjectFeaturesCompatibility
extend ActiveSupport::Concern
+ # TODO: remove in API v5, replaced by *_access_level
def wiki_enabled=(value)
- write_feature_attribute(:wiki_access_level, value)
+ write_feature_attribute_boolean(:wiki_access_level, value)
end
+ # TODO: remove in API v5, replaced by *_access_level
def builds_enabled=(value)
- write_feature_attribute(:builds_access_level, value)
+ write_feature_attribute_boolean(:builds_access_level, value)
end
+ # TODO: remove in API v5, replaced by *_access_level
def merge_requests_enabled=(value)
- write_feature_attribute(:merge_requests_access_level, value)
+ write_feature_attribute_boolean(:merge_requests_access_level, value)
end
+ # TODO: remove in API v5, replaced by *_access_level
def issues_enabled=(value)
- write_feature_attribute(:issues_access_level, value)
+ write_feature_attribute_boolean(:issues_access_level, value)
end
+ # TODO: remove in API v5, replaced by *_access_level
def snippets_enabled=(value)
- write_feature_attribute(:snippets_access_level, value)
+ write_feature_attribute_boolean(:snippets_access_level, value)
+ end
+
+ def repository_access_level=(value)
+ write_feature_attribute_string(:repository_access_level, value)
+ end
+
+ def wiki_access_level=(value)
+ write_feature_attribute_string(:wiki_access_level, value)
+ end
+
+ def builds_access_level=(value)
+ write_feature_attribute_string(:builds_access_level, value)
+ end
+
+ def merge_requests_access_level=(value)
+ write_feature_attribute_string(:merge_requests_access_level, value)
+ end
+
+ def issues_access_level=(value)
+ write_feature_attribute_string(:issues_access_level, value)
+ end
+
+ def snippets_access_level=(value)
+ write_feature_attribute_string(:snippets_access_level, value)
end
private
- def write_feature_attribute(field, value)
+ def write_feature_attribute_boolean(field, value)
+ access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
+ write_feature_attribute_raw(field, access_level)
+ end
+
+ def write_feature_attribute_string(field, value)
+ access_level = ProjectFeature.access_level_from_str(value)
+ write_feature_attribute_raw(field, access_level)
+ end
+
+ def write_feature_attribute_raw(field, value)
build_project_feature unless project_feature
- access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
- project_feature.__send__(:write_attribute, field, access_level) # rubocop:disable GitlabSecurity/PublicSend
+ project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index 5b3880f94ba..d91be73d6f0 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -173,7 +173,11 @@ 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)
diff --git a/app/models/discussion.rb b/app/models/discussion.rb
index ae13cdfd85f..dd896f77084 100644
--- a/app/models/discussion.rb
+++ b/app/models/discussion.rb
@@ -38,6 +38,17 @@ class Discussion
grouped_notes.values.map { |notes| build(notes, context_noteable) }
end
+ def self.lazy_find(discussion_id)
+ BatchLoader.for(discussion_id).batch do |discussion_ids, loader|
+ results = Note.where(discussion_id: discussion_ids).fresh.to_a.group_by(&:discussion_id)
+ results.each do |discussion_id, notes|
+ next if notes.empty?
+
+ loader.call(discussion_id, Discussion.build(notes))
+ end
+ end
+ end
+
# Returns an alphanumeric discussion ID based on `build_discussion_id`
def self.discussion_id(note)
Digest::SHA1.hexdigest(build_discussion_id(note).join("-"))
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 e96e26cc773..53977748c30 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1127,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/note.rb b/app/models/note.rb
index 4e9ea146485..5c31cff9816 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -158,6 +158,8 @@ class Note < ApplicationRecord
Discussion.build_collection(all.includes(:noteable).fresh, context_noteable)
end
+ # Note: Where possible consider using Discussion#lazy_find to return
+ # Discussions in order to benefit from having records batch loaded.
def find_discussion(discussion_id)
notes = where(discussion_id: discussion_id).fresh.to_a
diff --git a/app/models/project.rb b/app/models/project.rb
index 0f4fba5d0b6..bfc35b77b8f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -15,6 +15,7 @@ class Project < ApplicationRecord
include CaseSensitivity
include TokenAuthenticatable
include ValidAttribute
+ include ProjectAPICompatibility
include ProjectFeaturesCompatibility
include SelectForProjectAuthorization
include Presentable
@@ -2144,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_feature.rb b/app/models/project_feature.rb
index 0542581c6e0..7ff06655de0 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -24,6 +24,12 @@ class ProjectFeature < ApplicationRecord
FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
PRIVATE_FEATURES_MIN_ACCESS_LEVEL = { merge_requests: Gitlab::Access::REPORTER }.freeze
+ STRING_OPTIONS = HashWithIndifferentAccess.new({
+ 'disabled' => DISABLED,
+ 'private' => PRIVATE,
+ 'enabled' => ENABLED,
+ 'public' => PUBLIC
+ }).freeze
class << self
def access_level_attribute(feature)
@@ -45,6 +51,14 @@ class ProjectFeature < ApplicationRecord
PRIVATE_FEATURES_MIN_ACCESS_LEVEL.fetch(feature, Gitlab::Access::GUEST)
end
+ def access_level_from_str(level)
+ STRING_OPTIONS.fetch(level)
+ end
+
+ def str_from_access_level(level)
+ STRING_OPTIONS.key(level)
+ end
+
private
def ensure_feature!(feature)
@@ -83,6 +97,10 @@ class ProjectFeature < ApplicationRecord
public_send(ProjectFeature.access_level_attribute(feature)) # rubocop:disable GitlabSecurity/PublicSend
end
+ def string_access_level(feature)
+ ProjectFeature.str_from_access_level(access_level(feature))
+ end
+
def builds_enabled?
builds_access_level > DISABLED
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 a408db7ebbe..a25d5abfa64 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -845,6 +845,10 @@ class Repository
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)
their_commit_id = commit(source)&.id
raise 'Invalid merge source' if their_commit_id.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/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index dedab98b56d..9d4cf5df713 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -6,7 +6,7 @@ module Ci
class RegisterJobService
attr_reader :runner
- JOB_QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300].freeze
+ JOB_QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300, 900].freeze
JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET = 5.freeze
Result = Struct.new(:build, :valid?)
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/merge_base_service.rb b/app/services/merge_requests/merge_base_service.rb
index 095bdca5472..1ed396cee1e 100644
--- a/app/services/merge_requests/merge_base_service.rb
+++ b/app/services/merge_requests/merge_base_service.rb
@@ -28,6 +28,17 @@ module MergeRequests
private
+ def check_source
+ unless source
+ raise_error('No source for merge')
+ end
+ end
+
+ # Overridden in EE.
+ def check_size_limit
+ # No-op
+ end
+
# Overridden in EE.
def error_check!
# No-op
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index d8a78001b79..3e0f5aa181c 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -48,13 +48,13 @@ module MergeRequests
def error_check!
super
+ check_source
+
error =
if @merge_request.should_be_rebased?
'Only fast-forward merge is allowed for your project. Please update your source branch'
elsif !@merge_request.mergeable?
'Merge request is not mergeable'
- elsif !source
- 'No source for merge'
end
raise_error(error) if error
diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb
index 0ea50a5dbf5..37b5805ae7e 100644
--- a/app/services/merge_requests/merge_to_ref_service.rb
+++ b/app/services/merge_requests/merge_to_ref_service.rb
@@ -16,7 +16,7 @@ module MergeRequests
def execute(merge_request)
@merge_request = merge_request
- validate!
+ error_check!
commit_id = commit
@@ -39,21 +39,9 @@ module MergeRequests
merge_request.diff_head_sha
end
- def validate!
- error_check!
- end
-
+ override :error_check!
def error_check!
- super
-
- error =
- if !hooks_validation_pass?(merge_request)
- hooks_validation_error(merge_request)
- elsif source.blank?
- 'No source for merge'
- end
-
- raise_error(error) if error
+ check_source
end
##
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/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/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/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/62826-graphql-note-mutations.yml b/changelogs/unreleased/62826-graphql-note-mutations.yml
new file mode 100644
index 00000000000..85273186bb6
--- /dev/null
+++ b/changelogs/unreleased/62826-graphql-note-mutations.yml
@@ -0,0 +1,5 @@
+---
+title: GraphQL mutations for managing Notes
+merge_request: 30210
+author:
+type: added
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/64066-fix-uneven-click-areas.yml b/changelogs/unreleased/64066-fix-uneven-click-areas.yml
new file mode 100644
index 00000000000..ce0572cad34
--- /dev/null
+++ b/changelogs/unreleased/64066-fix-uneven-click-areas.yml
@@ -0,0 +1,5 @@
+---
+title: Fix spacing issues for toasts
+merge_request: 30345
+author:
+type: fixed
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/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-unicorn-sampler-workers-count.yml b/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml
new file mode 100644
index 00000000000..9a263b7f460
--- /dev/null
+++ b/changelogs/unreleased/fix-unicorn-sampler-workers-count.yml
@@ -0,0 +1,5 @@
+---
+title: Make unicorn_workers to return meaningful results
+merge_request: 30506
+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/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/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/project_api.yml b/changelogs/unreleased/project_api.yml
new file mode 100644
index 00000000000..a04f9bb5608
--- /dev/null
+++ b/changelogs/unreleased/project_api.yml
@@ -0,0 +1,5 @@
+---
+title: Improve Project API
+merge_request: 28327
+author: Mathieu Parent
+type: added
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/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/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/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/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/danger/only_documentation/Dangerfile b/danger/only_documentation/Dangerfile
index 0e5f841c891..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/', '.gitlab/ci/docs.gitlab-ci.yml') }
+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/doc/administration/audit_events.md b/doc/administration/audit_events.md
index ae1f6e00ea5..a80ff330e03 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -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 daba5b4ca82..65d36612d85 100644
--- a/doc/administration/auditor_users.md
+++ b/doc/administration/auditor_users.md
@@ -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/smartcard.md b/doc/administration/auth/smartcard.md
index 2f3289d6102..a0d4e9ef3b5 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -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 e76d3b14a40..dc4cc401fca 100644
--- a/doc/administration/database_load_balancing.md
+++ b/doc/administration/database_load_balancing.md
@@ -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.
diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md
index 5cfa6bcae56..f0d329d5296 100644
--- a/doc/administration/geo/replication/index.md
+++ b/doc/administration/geo/replication/index.md
@@ -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/security_review.md b/doc/administration/geo/replication/security_review.md
index 7c21a348e23..ed3f1faa93e 100644
--- a/doc/administration/geo/replication/security_review.md
+++ b/doc/administration/geo/replication/security_review.md
@@ -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/gitaly/index.md b/doc/administration/gitaly/index.md
index b98383f30fc..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
@@ -436,8 +436,8 @@ 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.
diff --git a/doc/administration/high_availability/consul.md b/doc/administration/high_availability/consul.md
index 3bdce7bce5e..49199b659bc 100644
--- a/doc/administration/high_availability/consul.md
+++ b/doc/administration/high_availability/consul.md
@@ -6,6 +6,72 @@ As part of its High Availability stack, GitLab Premium includes a bundled versio
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 d7acc1e7eb1..c32a73080ff 100644
--- a/doc/administration/high_availability/database.md
+++ b/doc/administration/high_availability/database.md
@@ -31,7 +31,7 @@ deploy the bundled PostgreSQL.
### 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
@@ -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
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/redis.md b/doc/administration/high_availability/redis.md
index 984c9688cdf..27310c59755 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -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.
@@ -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 53a61358670..73a39a6dd35 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -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 c5909e700c5..00c8863f200 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -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).
diff --git a/doc/administration/issue_closing_pattern.md b/doc/administration/issue_closing_pattern.md
index f2877c49d05..e1bbabb2878 100644
--- a/doc/administration/issue_closing_pattern.md
+++ b/doc/administration/issue_closing_pattern.md
@@ -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,9 +38,9 @@ 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.
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/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index c328d4c3aaa..99cd9051778 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -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,21 +41,21 @@ 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
`/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.
@@ -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,18 +131,18 @@ 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.
@@ -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/packages.md b/doc/administration/packages.md
index cc7d76d61f6..c0f8777a8c0 100644
--- a/doc/administration/packages.md
+++ b/doc/administration/packages.md
@@ -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/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/api/README.md b/doc/api/README.md
index 6284bc82e27..3ded230370c 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -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 926a0be1d8b..08ec1d832df 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -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
```
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/epics.md b/doc/api/epics.md
index 563f6feb546..d05eb0a8804 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -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/graphql/index.md b/doc/api/graphql/index.md
index 3bf96e279b7..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,23 +23,13 @@ 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
diff --git a/doc/api/issue_links.md b/doc/api/issue_links.md
index 31776568b8e..280431fa87c 100644
--- a/doc/api/issue_links.md
+++ b/doc/api/issue_links.md
@@ -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 6952426c322..23126a05b66 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -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
@@ -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
@@ -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
diff --git a/doc/api/issues_statistics.md b/doc/api/issues_statistics.md
index 58a32e879d7..d7edb296be2 100644
--- a/doc/api/issues_statistics.md
+++ b/doc/api/issues_statistics.md
@@ -43,7 +43,7 @@ GET /issues_statistics?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_statistics
@@ -99,7 +99,7 @@ GET /groups/:id/issues_statistics?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_statistics
@@ -155,8 +155,7 @@ GET /projects/:id/issues_statistics?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/projects/4/issues_statistics
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index bb8df6187a9..1add5f432ac 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -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 02754d63bd4..12f1d03d576 100644
--- a/doc/api/license.md
+++ b/doc/api/license.md
@@ -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 9521be5abac..1af7567626f 100644
--- a/doc/api/managed_licenses.md
+++ b/doc/api/managed_licenses.md
@@ -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 504ce56f569..c211916464a 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -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,
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 1a795c0cfa7..ca90771b085 100644
--- a/doc/api/packages.md
+++ b/doc/api/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/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_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 69d61b3974b..cc932e3ef58 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -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 da05b090699..781192fb92e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -708,11 +708,17 @@ POST /projects
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
-| `issues_enabled` | boolean | no | Enable issues for this project |
-| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
-| `jobs_enabled` | boolean | no | Enable jobs for this project |
-| `wiki_enabled` | boolean | no | Enable wiki for this project |
-| `snippets_enabled` | boolean | no | Enable snippets for this project |
+| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
+| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
+| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
+| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
+| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
+| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
@@ -721,13 +727,19 @@ POST /projects
| `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
-| `merge_method` | string | no | Set the merge method used |
+| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
+| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
+| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
+| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
+| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file |
+| `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 |
@@ -753,11 +765,17 @@ POST /projects/user/:user_id
| `path` | string | no | Custom repository name for new project. By default generated based on name |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `description` | string | no | Short project description |
-| `issues_enabled` | boolean | no | Enable issues for this project |
-| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
-| `jobs_enabled` | boolean | no | Enable jobs for this project |
-| `wiki_enabled` | boolean | no | Enable wiki for this project |
-| `snippets_enabled` | boolean | no | Enable snippets for this project |
+| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
+| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
+| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
+| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
+| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
+| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
@@ -766,13 +784,19 @@ POST /projects/user/:user_id
| `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
-| `merge_method` | string | no | Set the merge method used |
+| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project |
| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
+| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
+| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
+| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
+| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file |
+| `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 |
@@ -798,11 +822,17 @@ PUT /projects/:id
| `path` | string | no | Custom repository name for the project. By default generated based on name |
| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description |
-| `issues_enabled` | boolean | no | Enable issues for this project |
-| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
-| `jobs_enabled` | boolean | no | Enable jobs for this project |
-| `wiki_enabled` | boolean | no | Enable wiki for this project |
-| `snippets_enabled` | boolean | no | Enable snippets for this project |
+| `issues_enabled` | boolean | no | (deprecated) Enable issues for this project. Use `issues_access_level` instead |
+| `merge_requests_enabled` | boolean | no | (deprecated) Enable merge requests for this project. Use `merge_requests_access_level` instead |
+| `jobs_enabled` | boolean | no | (deprecated) Enable jobs for this project. Use `builds_access_level` instead |
+| `wiki_enabled` | boolean | no | (deprecated) Enable wiki for this project. Use `wiki_access_level` instead |
+| `snippets_enabled` | boolean | no | (deprecated) Enable snippets for this project. Use `snippets_access_level` instead |
+| `issues_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `repository_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `merge_requests_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `builds_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
+| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
@@ -811,13 +841,19 @@ PUT /projects/:id
| `public_builds` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
-| `merge_method` | string | no | Set the merge method used |
+| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
| `tag_list` | array | no | The list of tags for a project; put array of tags, that should be finally assigned to a project |
| `avatar` | mixed | no | Image file for avatar of the project |
+| `build_git_strategy` | string | no | The Git strategy. Defaults to `fetch` |
+| `build_timeout` | integer | no | The maximum amount of time in minutes that a job is able run (in seconds) |
+| `auto_cancel_pending_pipelines` | string | no | Auto-cancel pending pipelines (Note: this is not a boolean, but enabled/disabled |
+| `build_coverage_regex` | string | no | Test coverage parsing |
| `ci_config_path` | string | no | The path to CI config file |
| `ci_default_git_depth` | integer | no | Default number of revisions for [shallow cloning](../user/project/pipelines/settings.md#git-shallow-clone) |
+| `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 |
@@ -1631,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",
@@ -1663,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
@@ -1684,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
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 6cd5f6862d3..4aff79c9c62 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/runners.md b/doc/api/runners.md
index 1318b9ca828..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.
diff --git a/doc/api/scim.md b/doc/api/scim.md
index 46498465261..ece7f56e394 100644
--- a/doc/api/scim.md
+++ b/doc/api/scim.md
@@ -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/users.md b/doc/api/users.md
index 4e427766750..213d1865aca 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -164,6 +164,7 @@ the `group_saml` provider option:
...
}
]
+```
You can lookup users by external UID and provider:
@@ -592,6 +593,30 @@ 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).
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 816bd35018a..2d7fb323d79 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -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/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/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 c7f8792a830..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/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/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 8319603fea2..b645a72567c 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -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).
diff --git a/doc/development/chatops_on_gitlabcom.md b/doc/development/chatops_on_gitlabcom.md
index de942c0ae94..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](../ci/chatops/README.md)
- - [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 27c349c03aa..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
@@ -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/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/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 7127efb7405..d9cea0614c3 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -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>
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index dc17b59a9a0..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.
@@ -1038,8 +1038,8 @@ to avoid conflicts during CE to EE merge.
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/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/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/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 7d0a6f6acda..80ec7b8c0cf 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -1,18 +1,18 @@
# 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 418efd9eef9..08aa0b08525 100644
--- a/doc/development/packages.md
+++ b/doc/development/packages.md
@@ -5,11 +5,11 @@ This document will guide you through adding another [package management system](
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/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/index.md b/doc/development/testing_guide/index.md
index c4b18391cb2..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,7 +80,6 @@ Everything you should know about how to run end-to-end tests using
[Return to Development documentation](../README.md)
-[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/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index 2070187255a..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`.
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/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/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 9a8cd925e48..33b23372280 100644
--- a/doc/intro/README.md
+++ b/doc/intro/README.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/raketasks/README.md b/doc/raketasks/README.md
index b28f21f83a9..dcc96507676 100644
--- a/doc/raketasks/README.md
+++ b/doc/raketasks/README.md
@@ -9,7 +9,7 @@ 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
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index e2d51f673b5..503ad784a77 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -316,7 +316,7 @@ If a project's repository contains a `Dockerfile`, Auto Build will use
If you are also using Auto Review Apps and Auto Deploy and choose to provide
your own `Dockerfile`, make sure you expose your application to port
`5000` as this is the port assumed by the
-[default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app).
+[default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app). Alternatively you can override the default values by [customizing the Auto Deploy helm chart](#custom-helm-chart)
#### Auto Build using Heroku buildpacks
@@ -452,7 +452,7 @@ be deleted.
Review apps are deployed using the
[auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with
-Helm. The app will be deployed into the [Kubernetes
+Helm, which can be [customized](#custom-helm-chart). The app will be deployed into the [Kubernetes
namespace](../../user/project/clusters/index.md#deployment-variables)
for the environment.
@@ -514,7 +514,7 @@ Auto Deploy doesn't include deployments to staging or canary by default, but the
enable them.
You can make use of [environment variables](#environment-variables) to automatically
-scale your pod replicas.
+scale your pod replicas and to apply custom arguments to the Auto DevOps `helm upgrade` commands. This is an easy way to [customize the Auto Deploy helm chart](#custom-helm-chart).
Apps are deployed using the
[auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with
@@ -655,6 +655,9 @@ 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.
+You can also make use of the `HELM_UPGRADE_EXTRA_ARGS` environment variable to override the default values in the `values.yaml` file in the [default Helm chart](https://gitlab.com/gitlab-org/charts/auto-deploy-app).
+To apply your own `values.yaml` file to all Helm upgrade commands in Auto Deploy set `HELM_UPGRADE_EXTRA_ARGS` to `--values my-values.yaml`.
+
### Custom Helm chart per environment **(PREMIUM)**
You can specify the use of a custom Helm chart per environment by scoping the environment variable
@@ -761,7 +764,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. |
| `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. |
| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. |
-| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. |
+| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](https://docs.gitlab.com/ee/topics/autodevops/index.html#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. |
TIP: **Tip:**
Set up the replica variables using a
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/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 db853a58b91..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)**
-> [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/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index e920628d2c0..4fde7477490 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -5,7 +5,7 @@ type: reference
# 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/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md
index 2d861a0e15b..f2ba131d17b 100644
--- a/doc/user/admin_area/settings/instance_template_repository.md
+++ b/doc/user/admin_area/settings/instance_template_repository.md
@@ -5,7 +5,7 @@ type: reference
# 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/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 f2156720af7..c6bc580fb8f 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -444,7 +444,7 @@ Clicking on the **Reply to comment** button will bring the reply area into focus
![Reply to comment feature](img/reply_to_comment.gif)
-Relying to a non-discussion comment will convert the non-discussion comment to a
+Replying to a non-discussion comment will convert the non-discussion comment to a
threaded discussion once the reply is submitted. This conversion is considered an edit
to the original comment, so a note about when it was last edited will appear underneath it.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 769a22315be..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
@@ -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 |
diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md
index e74644710a5..8a85c375490 100644
--- a/doc/user/group/custom_project_templates.md
+++ b/doc/user/group/custom_project_templates.md
@@ -4,7 +4,7 @@ type: reference
# 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/roadmap/index.md b/doc/user/group/roadmap/index.md
index 672149f8217..a72cd990706 100644
--- a/doc/user/group/roadmap/index.md
+++ b/doc/user/group/roadmap/index.md
@@ -4,7 +4,7 @@ type: reference
# 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/project/clusters/index.md b/doc/user/project/clusters/index.md
index 43f9ee7b621..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
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/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/integrations/github.md b/doc/user/project/integrations/github.md
index e8ffbde1f57..d0399f9193b 100644
--- a/doc/user/project/integrations/github.md
+++ b/doc/user/project/integrations/github.md
@@ -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/prometheus.md b/doc/user/project/integrations/prometheus.md
index 6e02761e041..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
diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md
index 01da7e00d74..5049733abdd 100644
--- a/doc/user/project/integrations/prometheus_library/cloudwatch.md
+++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md
@@ -20,7 +20,7 @@ The [Prometheus service](../prometheus.md) must be enabled.
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/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/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index e95ab7d02c4..49b9826a52a 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -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/operations/tracing.md b/doc/user/project/operations/tracing.md
index a74233bbd02..b92d2e49839 100644
--- a/doc/user/project/operations/tracing.md
+++ b/doc/user/project/operations/tracing.md
@@ -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/npm_registry.md b/doc/user/project/packages/npm_registry.md
index b2cfe10836f..481b1ce0337 100644
--- a/doc/user/project/packages/npm_registry.md
+++ b/doc/user/project/packages/npm_registry.md
@@ -11,11 +11,6 @@ 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:**
@@ -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/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/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index 3d2ff0444eb..55183408a10 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/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/lib/api/api.rb b/lib/api/api.rb
index 42499c5b41e..7016a66593d 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -166,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/entities.rb b/lib/api/entities.rb
index b9aa387ba61..765819e6bf1 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -201,6 +201,7 @@ module API
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
projects_relation.preload(:project_feature, :route)
.preload(:import_state, :tags)
+ .preload(:auto_devops)
.preload(namespace: [:route, :owner])
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -247,12 +248,20 @@ module API
expose :container_registry_enabled
# Expose old field names with the new permissions methods to keep API compatible
+ # TODO: remove in API v5, replaced by *_access_level
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
+ expose(:issues_access_level) { |project, options| project.project_feature.string_access_level(:issues) }
+ expose(:repository_access_level) { |project, options| project.project_feature.string_access_level(:repository) }
+ expose(:merge_requests_access_level) { |project, options| project.project_feature.string_access_level(:merge_requests) }
+ expose(:wiki_access_level) { |project, options| project.project_feature.string_access_level(:wiki) }
+ expose(:builds_access_level) { |project, options| project.project_feature.string_access_level(:builds) }
+ expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
+
expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
@@ -267,6 +276,12 @@ module API
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :ci_default_git_depth
expose :public_builds, as: :public_jobs
+ expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options|
+ project.build_allow_git_fetch ? 'fetch' : 'clone'
+ end
+ expose :build_timeout
+ expose :auto_cancel_pending_pipelines
+ expose :build_coverage_regex
expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links, options)
@@ -280,6 +295,10 @@ module API
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
}
expose :external_authorization_classification_label
+ expose :auto_devops_enabled?, as: :auto_devops_enabled
+ expose :auto_devops_deploy_strategy do |project, options|
+ project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
+ end
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
@@ -289,6 +308,7 @@ module API
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555
super(projects_relation).preload(:group)
.preload(:ci_cd_settings)
+ .preload(:auto_devops)
.preload(project_group_links: { group: :route },
fork_network: :root_project,
fork_network_member: :forked_from_project,
@@ -491,7 +511,7 @@ module API
end
end
- class ProjectEntity < Grape::Entity
+ class IssuableEntity < Grape::Entity
expose :id, :iid
expose(:project_id) { |entity| entity&.project.try(:id) }
expose :title, :description
@@ -544,7 +564,7 @@ module API
end
end
- class IssueBasic < ProjectEntity
+ class IssueBasic < IssuableEntity
expose :closed_at
expose :closed_by, using: Entities::UserBasic
@@ -650,14 +670,14 @@ module API
end
end
- class MergeRequestSimple < ProjectEntity
+ class MergeRequestSimple < IssuableEntity
expose :title
expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request)
end
end
- class MergeRequestBasic < ProjectEntity
+ class MergeRequestBasic < IssuableEntity
expose :merged_by, using: Entities::UserBasic do |merge_request, _options|
merge_request.metrics&.merged_by
end
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/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index f242f1fea0e..0e21a7a66fd 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -8,12 +8,26 @@ module API
params :optional_project_params_ce do
optional :description, type: String, desc: 'The description of the project'
+ optional :build_git_strategy, type: String, values: %w(fetch clone), desc: 'The Git strategy. Defaults to `fetch`'
+ optional :build_timeout, type: Integer, desc: 'Build timeout'
+ optional :auto_cancel_pending_pipelines, type: String, values: %w(disabled enabled), desc: 'Auto-cancel pending pipelines'
+ optional :build_coverage_regex, type: String, desc: 'Test coverage parsing'
optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`'
+
+ # TODO: remove in API v5, replaced by *_access_level
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled'
optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
+
+ optional :issues_access_level, type: String, values: %w(disabled private enabled), desc: 'Issues access level. One of `disabled`, `private` or `enabled`'
+ optional :repository_access_level, type: String, values: %w(disabled private enabled), desc: 'Repository access level. One of `disabled`, `private` or `enabled`'
+ optional :merge_requests_access_level, type: String, values: %w(disabled private enabled), desc: 'Merge requests access level. One of `disabled`, `private` or `enabled`'
+ optional :wiki_access_level, type: String, values: %w(disabled private enabled), desc: 'Wiki access level. One of `disabled`, `private` or `enabled`'
+ optional :builds_access_level, type: String, values: %w(disabled private enabled), desc: 'Builds access level. One of `disabled`, `private` or `enabled`'
+ optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`'
+
optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push'
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
@@ -30,6 +44,8 @@ module API
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
+ optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
+ optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy'
end
params :optional_project_params_ee do
@@ -48,15 +64,20 @@ module API
def self.update_params_at_least_one_of
[
- :jobs_enabled,
- :resolve_outdated_diff_discussions,
+ :auto_devops_enabled,
+ :auto_devops_deploy_strategy,
+ :auto_cancel_pending_pipelines,
+ :build_coverage_regex,
+ :build_git_strategy,
+ :build_timeout,
+ :builds_access_level,
:ci_config_path,
:container_registry_enabled,
:default_branch,
:description,
- :issues_enabled,
+ :issues_access_level,
:lfs_enabled,
- :merge_requests_enabled,
+ :merge_requests_access_level,
:merge_method,
:name,
:only_allow_merge_if_all_discussions_are_resolved,
@@ -64,14 +85,24 @@ module API
:path,
:printing_merge_request_link_enabled,
:public_builds,
+ :repository_access_level,
:request_access_enabled,
+ :resolve_outdated_diff_discussions,
:shared_runners_enabled,
- :snippets_enabled,
+ :snippets_access_level,
:tag_list,
:visibility,
- :wiki_enabled,
+ :wiki_access_level,
:avatar,
- :external_authorization_classification_label
+ :external_authorization_classification_label,
+
+ # TODO: remove in API v5, replaced by *_access_level
+ :issues_enabled,
+ :jobs_enabled,
+ :merge_requests_enabled,
+ :wiki_enabled,
+ :jobs_enabled,
+ :snippets_enabled
]
end
end
diff --git a/lib/api/import_github.rb b/lib/api/import_github.rb
index e7504051808..21d4928193e 100644
--- a/lib/api/import_github.rb
+++ b/lib/api/import_github.rb
@@ -28,7 +28,7 @@ module API
desc 'Import a GitHub project' do
detail 'This feature was introduced in GitLab 11.3.4.'
- success Entities::ProjectEntity
+ success ::ProjectEntity
end
params do
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
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/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/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/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 9e3de910e3c..cc9503fb6de 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -30,6 +30,7 @@ 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
@@ -378,6 +379,46 @@ 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)
+ false
+ # 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/global_id.rb b/lib/gitlab/global_id.rb
new file mode 100644
index 00000000000..cc82b6c5897
--- /dev/null
+++ b/lib/gitlab/global_id.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GlobalId
+ def self.build(object = nil, model_name: nil, id: nil, params: nil)
+ if object
+ model_name ||= object.class.name
+ id ||= object.id
+ end
+
+ ::URI::GID.build(app: GlobalID.app, model_name: model_name, model_id: id, params: params)
+ end
+ end
+end
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/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
index 9af7e0afed4..355f938704e 100644
--- a/lib/gitlab/metrics/samplers/unicorn_sampler.rb
+++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
@@ -54,7 +54,16 @@ module Gitlab
end
def unicorn_workers_count
- `pgrep -f '[u]nicorn_rails worker.+ #{Rails.root.to_s}'`.split.count
+ http_servers.sum(&:worker_processes) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ # Traversal of ObjectSpace is expensive, on fully loaded application
+ # it takes around 80ms. The instances of HttpServers are not a subject
+ # to change so we can cache the list of servers.
+ def http_servers
+ return [] unless defined?(::Unicorn::HttpServer)
+
+ @http_servers ||= ObjectSpace.each_object(::Unicorn::HttpServer).to_a
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 07534e9c941..9b6e8d8c8a4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -124,9 +124,6 @@ msgstr ""
msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
-msgid "%{counter_repositories} repositories, %{counter_wikis} wikis, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS"
-msgstr ""
-
msgid "%{count} more"
msgstr ""
@@ -8877,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 ""
@@ -11224,10 +11224,10 @@ msgstr ""
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
msgstr ""
-msgid "To protect this issues confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private."
+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 issues confidentiality, a private fork of this project was selected."
+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."
diff --git a/package.json b/package.json
index 5b255f55e33..5955790488a 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.67.0",
- "@gitlab/ui": "^5.3.2",
+ "@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/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..e78b9bece19 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 = {
@@ -173,8 +177,8 @@ module QA
ENV.fetch("GCLOUD_ACCOUNT_EMAIL")
end
- def gcloud_zone
- ENV.fetch('GCLOUD_ZONE')
+ def gcloud_region
+ ENV.fetch('GCLOUD_REGION')
end
def has_gcloud_credentials?
@@ -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/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index 16a89591eb2..40263e94065 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -28,17 +28,17 @@ module QA
create #{cluster_name}
#{auth_options}
--enable-basic-auth
- --zone #{Runtime::Env.gcloud_zone}
+ --region #{Runtime::Env.gcloud_region}
&& gcloud container clusters
get-credentials
- --zone #{Runtime::Env.gcloud_zone}
+ --region #{Runtime::Env.gcloud_region}
#{cluster_name}
CMD
@api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'`
@admin_user = "#{cluster_name}-admin"
- master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --zone #{Runtime::Env.gcloud_zone} --format 'json(masterAuth.username, masterAuth.password)'`)
+ master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --region #{Runtime::Env.gcloud_region} --format 'json(masterAuth.username, masterAuth.password)'`)
shell <<~CMD.tr("\n", ' ')
kubectl config set-credentials #{@admin_user}
--username #{master_auth['masterAuth']['username']}
@@ -66,10 +66,10 @@ module QA
def remove!
shell <<~CMD.tr("\n", ' ')
gcloud container clusters delete
- --zone #{Runtime::Env.gcloud_zone}
- #{cluster_name}
- --quiet --async
- CMD
+ --region #{Runtime::Env.gcloud_region}
+ #{cluster_name}
+ --quiet --async
+ CMD
end
private
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
index a9de64e357a..2363836d5e3 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
@@ -7,15 +7,15 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- source_group = Resource::Group.fabricate! do |group|
+ source_group = Resource::Group.fabricate_via_api! do |group|
group.path = 'source-group'
end
- target_group = Resource::Group.fabricate! do |group|
+ target_group = Resource::Group.fabricate_via_api! do |group|
group.path = 'target-group'
end
- project = Resource::Project.fabricate! do |project|
+ project = Resource::Project.fabricate_via_api! do |project|
project.group = source_group
project.name = 'transfer-project'
project.initialize_with_readme = true
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
index d1747280227..f51c16f472c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -9,7 +9,7 @@ module QA
user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
- project = Resource::Project.fabricate! do |resource|
+ project = Resource::Project.fabricate_via_api! do |resource|
resource.name = 'add-member-project'
end
project.visit!
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/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/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/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/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/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
index a241c764df7..fd307ce5ab3 100644
--- 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
@@ -19,7 +19,7 @@ exports[`Confidential merge request project form group component renders empty s
<br />
<span>
- To protect this issues confidentiality,
+ To protect this issue's confidentiality,
<a
class="help-link"
href="https://test.com"
@@ -28,23 +28,6 @@ exports[`Confidential merge request project form group component renders empty s
</a>
and set the forks visiblity to private.
</span>
-
- <gllink-stub
- class="help-link"
- href="/help"
- target="_blank"
- >
- <span
- class="sr-only"
- >
- Read more
- </span>
-
- <i
- aria-hidden="true"
- class="fa fa-question-circle"
- />
- </gllink-stub>
</p>
</div>
</div>
@@ -69,7 +52,7 @@ exports[`Confidential merge request project form group component renders fork dr
<br />
<span>
- To protect this issues confidentiality,
+ To protect this issue's confidentiality,
<a
class="help-link"
href="https://test.com"
@@ -78,23 +61,6 @@ exports[`Confidential merge request project form group component renders fork dr
</a>
and set the forks visiblity to private.
</span>
-
- <gllink-stub
- class="help-link"
- href="/help"
- target="_blank"
- >
- <span
- class="sr-only"
- >
- Read more
- </span>
-
- <i
- aria-hidden="true"
- class="fa fa-question-circle"
- />
- </gllink-stub>
</p>
</div>
</div>
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 93b86b9b812..dec6b23d72a 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -143,6 +143,26 @@ describe GitlabSchema do
end
end
+ context 'for classes that are not ActiveRecord subclasses and have implemented .lazy_find' do
+ it 'returns the correct record' do
+ note = create(:discussion_note_on_merge_request)
+
+ result = described_class.object_from_id(note.to_global_id)
+
+ expect(result.__sync).to eq(note)
+ end
+
+ it 'batchloads the queries' do
+ note1 = create(:discussion_note_on_merge_request)
+ note2 = create(:discussion_note_on_merge_request)
+
+ expect do
+ [described_class.object_from_id(note1.to_global_id),
+ described_class.object_from_id(note2.to_global_id)].map(&:__sync)
+ end.not_to exceed_query_limit(1)
+ end
+ end
+
context 'for other classes' do
# We cannot use an anonymous class here as `GlobalID` expects `.name` not
# to return `nil`
diff --git a/spec/graphql/types/diff_refs_type_spec.rb b/spec/graphql/types/diff_refs_type_spec.rb
new file mode 100644
index 00000000000..91017c827ad
--- /dev/null
+++ b/spec/graphql/types/diff_refs_type_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['DiffRefs'] do
+ it { expect(described_class.graphql_name).to eq('DiffRefs') }
+
+ it { expect(described_class).to have_graphql_fields(:base_sha, :head_sha, :start_sha) }
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index f73bd062369..59bd0123d88 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -13,7 +13,7 @@ describe GitlabSchema.types['MergeRequest'] do
description_html state created_at updated_at source_project target_project
project project_id source_project_id target_project_id source_branch
target_branch work_in_progress merge_when_pipeline_succeeds diff_head_sha
- merge_commit_sha user_notes_count should_remove_source_branch
+ merge_commit_sha user_notes_count should_remove_source_branch diff_refs
force_remove_source_branch merge_status in_progress_merge_commit_sha
merge_error allow_collaboration should_be_rebased rebase_commit_sha
rebase_in_progress merge_commit_message default_merge_commit_message
diff --git a/spec/graphql/types/notes/diff_position_type_spec.rb b/spec/graphql/types/notes/diff_position_type_spec.rb
index 2f8724d7f0d..345bca8f702 100644
--- a/spec/graphql/types/notes/diff_position_type_spec.rb
+++ b/spec/graphql/types/notes/diff_position_type_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe GitlabSchema.types['DiffPosition'] do
it 'exposes the expected fields' do
- expected_fields = [:head_sha, :base_sha, :start_sha, :file_path, :old_path,
+ expected_fields = [:diff_refs, :file_path, :old_path,
:new_path, :position_type, :old_line, :new_line, :x, :y,
:width, :height]
diff --git a/spec/graphql/types/notes/discussion_type_spec.rb b/spec/graphql/types/notes/discussion_type_spec.rb
index 2a1eb0efd35..ba7fc961212 100644
--- a/spec/graphql/types/notes/discussion_type_spec.rb
+++ b/spec/graphql/types/notes/discussion_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
describe GitlabSchema.types['Discussion'] do
- it { is_expected.to have_graphql_fields(:id, :created_at, :notes) }
+ it { is_expected.to have_graphql_fields(:id, :created_at, :notes, :reply_id) }
it { is_expected.to require_graphql_authorizations(:read_note) }
end
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/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/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/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/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/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/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/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/rugged_impl/use_rugged_spec.rb b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
new file mode 100644
index 00000000000..e7ef9d08f80
--- /dev/null
+++ b/spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb
@@ -0,0 +1,99 @@
+# 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
+ pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338')
+ 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
+ pending('temporary disabled because of https://gitlab.com/gitlab-org/gitlab-ce/issues/64338')
+ 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/global_id_spec.rb b/spec/lib/gitlab/global_id_spec.rb
new file mode 100644
index 00000000000..d35b5da0b75
--- /dev/null
+++ b/spec/lib/gitlab/global_id_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::GlobalId do
+ describe '.build' do
+ set(:object) { create(:issue) }
+
+ it 'returns a standard GlobalId if only object is passed' do
+ expect(described_class.build(object).to_s).to eq(object.to_global_id.to_s)
+ end
+
+ it 'returns a GlobalId from params' do
+ expect(described_class.build(model_name: 'MyModel', id: 'myid').to_s).to eq(
+ 'gid://gitlab/MyModel/myid'
+ )
+ end
+
+ it 'returns a GlobalId from object and `id` param' do
+ expect(described_class.build(object, id: 'myid').to_s).to eq(
+ 'gid://gitlab/Issue/myid'
+ )
+ end
+
+ it 'returns a GlobalId from object and `model_name` param' do
+ expect(described_class.build(object, model_name: 'MyModel').to_s).to eq(
+ "gid://gitlab/MyModel/#{object.id}"
+ )
+ end
+
+ it 'returns an error if model_name and id are not able to be determined' do
+ expect { described_class.build(id: 'myid') }.to raise_error(URI::InvalidComponentError)
+ expect { described_class.build(model_name: 'MyModel') }.to raise_error(URI::InvalidComponentError)
+ expect { described_class.build }.to raise_error(URI::InvalidComponentError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
index 090e456644f..4b697b2ba0f 100644
--- a/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/unicorn_sampler_spec.rb
@@ -4,7 +4,7 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
subject { described_class.new(1.second) }
describe '#sample' do
- let(:unicorn) { double('unicorn') }
+ let(:unicorn) { Module.new }
let(:raindrops) { double('raindrops') }
let(:stats) { double('stats') }
@@ -78,19 +78,32 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
end
end
- context 'additional metrics' do
- let(:unicorn_workers) { 2 }
-
+ context 'unicorn workers' do
before do
- allow(unicorn).to receive(:listener_names).and_return([""])
- allow(::Gitlab::Metrics::System).to receive(:cpu_time).and_return(3.14)
- allow(subject).to receive(:unicorn_workers_count).and_return(unicorn_workers)
+ allow(unicorn).to receive(:listener_names).and_return([])
end
- it "sets additional metrics" do
- expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, unicorn_workers)
+ context 'without http server' do
+ it "does set unicorn_workers to 0" do
+ expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, 0)
- subject.sample
+ subject.sample
+ end
+ end
+
+ context 'with http server' do
+ let(:http_server_class) { Struct.new(:worker_processes) }
+ let!(:http_server) { http_server_class.new(5) }
+
+ before do
+ stub_const('Unicorn::HttpServer', http_server_class)
+ end
+
+ it "sets additional metrics" do
+ expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, 5)
+
+ subject.sample
+ end
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/models/concerns/project_api_compatibility_spec.rb b/spec/models/concerns/project_api_compatibility_spec.rb
new file mode 100644
index 00000000000..8cecd4fe7bc
--- /dev/null
+++ b/spec/models/concerns/project_api_compatibility_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectAPICompatibility do
+ let(:project) { create(:project) }
+
+ # git_strategy
+ it "converts build_git_strategy=fetch to build_allow_git_fetch=true" do
+ project.update!(build_git_strategy: 'fetch')
+ expect(project.build_allow_git_fetch).to eq(true)
+ end
+
+ it "converts build_git_strategy=clone to build_allow_git_fetch=false" do
+ project.update!(build_git_strategy: 'clone')
+ expect(project.build_allow_git_fetch).to eq(false)
+ end
+
+ # auto_devops_enabled
+ it "converts auto_devops_enabled=false to auto_devops_enabled?=false" do
+ expect(project.auto_devops_enabled?).to eq(true)
+ project.update!(auto_devops_enabled: false)
+ expect(project.auto_devops_enabled?).to eq(false)
+ end
+
+ it "converts auto_devops_enabled=true to auto_devops_enabled?=true" do
+ expect(project.auto_devops_enabled?).to eq(true)
+ project.update!(auto_devops_enabled: true)
+ expect(project.auto_devops_enabled?).to eq(true)
+ end
+
+ # auto_devops_deploy_strategy
+ it "converts auto_devops_deploy_strategy=timed_incremental to auto_devops.deploy_strategy=timed_incremental" do
+ expect(project.auto_devops).to be_nil
+ project.update!(auto_devops_deploy_strategy: 'timed_incremental')
+ expect(project.auto_devops.deploy_strategy).to eq('timed_incremental')
+ end
+end
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index 5aa43b58217..1fe176ab5af 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -4,7 +4,8 @@ require 'spec_helper'
describe ProjectFeaturesCompatibility do
let(:project) { create(:project) }
- let(:features) { %w(issues wiki builds merge_requests snippets) }
+ let(:features_except_repository) { %w(issues wiki builds merge_requests snippets) }
+ let(:features) { features_except_repository + ['repository'] }
# We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table
# All those fields got moved to a new table called project_feature and are now integers instead of booleans
@@ -12,30 +13,37 @@ describe ProjectFeaturesCompatibility do
# So we can keep it compatible
it "converts fields from 'true' to ProjectFeature::ENABLED" do
- features.each do |feature|
+ features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, "true")
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED)
end
end
it "converts fields from 'false' to ProjectFeature::DISABLED" do
- features.each do |feature|
+ features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, "false")
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
end
end
it "converts fields from true to ProjectFeature::ENABLED" do
- features.each do |feature|
+ features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, true)
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED)
end
end
it "converts fields from false to ProjectFeature::DISABLED" do
- features.each do |feature|
+ features_except_repository.each do |feature|
project.update_attribute("#{feature}_enabled".to_sym, false)
expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
end
end
+
+ it "accepts private as ProjectFeature::PRIVATE" do
+ features.each do |feature|
+ project.update!("#{feature}_access_level".to_sym => 'private')
+ expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::PRIVATE)
+ end
+ end
end
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index e2ab9ddd4a5..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
diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb
index 22d4dab0617..950bdec4d00 100644
--- a/spec/models/discussion_spec.rb
+++ b/spec/models/discussion_spec.rb
@@ -10,6 +10,20 @@ describe Discussion do
let(:second_note) { create(:diff_note_on_merge_request, in_reply_to: first_note) }
let(:third_note) { create(:diff_note_on_merge_request) }
+ describe '.lazy_find' do
+ let!(:note1) { create(:discussion_note_on_merge_request).to_discussion }
+ let!(:note2) { create(:discussion_note_on_merge_request, in_reply_to: note1).to_discussion }
+
+ subject { [note1, note2].map { |note| described_class.lazy_find(note.discussion_id) } }
+
+ it 'batches requests' do
+ expect do
+ [described_class.lazy_find(note1.id),
+ described_class.lazy_find(note2.id)].map(&:__sync)
+ end.not_to exceed_query_limit(1)
+ end
+ end
+
describe '.build' do
it 'returns a discussion of the right type' do
discussion = described_class.build([first_note, second_note], merge_request)
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index fe6d68aff3f..9b0c232f370 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -3220,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/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/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb
new file mode 100644
index 00000000000..b04fcb9aece
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Adding a DiffNote' do
+ include GraphqlHelpers
+
+ set(:current_user) { create(:user) }
+ let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:project) { create(:project, :repository) }
+ let(:diff_refs) { noteable.diff_refs }
+ let(:mutation) do
+ variables = {
+ noteable_id: GitlabSchema.id_from_object(noteable).to_s,
+ body: 'Body text',
+ position: {
+ paths: {
+ old_path: 'files/ruby/popen.rb',
+ new_path: 'files/ruby/popen2.rb'
+ },
+ new_line: 14,
+ base_sha: diff_refs.base_sha,
+ head_sha: diff_refs.head_sha,
+ start_sha: diff_refs.start_sha
+ }
+ }
+
+ graphql_mutation(:create_diff_note, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:create_diff_note)
+ end
+
+ it_behaves_like 'a Note mutation when the user does not have permission'
+
+ context 'when the user has permission' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'a Note mutation that creates a Note'
+
+ it_behaves_like 'a Note mutation when there are active record validation errors', model: DiffNote
+
+ context do
+ let(:diff_refs) { build(:merge_request).diff_refs } # Allow fake diff refs so arguments are valid
+
+ it_behaves_like 'a Note mutation when the given resource id is not for a Noteable'
+ end
+
+ it 'returns the note with the correct position' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq('Body text')
+ mutation_position_response = mutation_response['note']['position']
+ expect(mutation_position_response['positionType']).to eq('text')
+ expect(mutation_position_response['filePath']).to eq('files/ruby/popen2.rb')
+ expect(mutation_position_response['oldPath']).to eq('files/ruby/popen.rb')
+ expect(mutation_position_response['newPath']).to eq('files/ruby/popen2.rb')
+ expect(mutation_position_response['newLine']).to eq(14)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb
new file mode 100644
index 00000000000..3ba6c689024
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Adding an image DiffNote' do
+ include GraphqlHelpers
+
+ set(:current_user) { create(:user) }
+ let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:project) { create(:project, :repository) }
+ let(:diff_refs) { noteable.diff_refs }
+ let(:mutation) do
+ variables = {
+ noteable_id: GitlabSchema.id_from_object(noteable).to_s,
+ body: 'Body text',
+ position: {
+ paths: {
+ old_path: 'files/images/any_image.png',
+ new_path: 'files/images/any_image2.png'
+ },
+ width: 100,
+ height: 200,
+ x: 1,
+ y: 2,
+ base_sha: diff_refs.base_sha,
+ head_sha: diff_refs.head_sha,
+ start_sha: diff_refs.start_sha
+ }
+ }
+
+ graphql_mutation(:create_image_diff_note, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:create_image_diff_note)
+ end
+
+ it_behaves_like 'a Note mutation when the user does not have permission'
+
+ context 'when the user has permission' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'a Note mutation that creates a Note'
+
+ it_behaves_like 'a Note mutation when there are active record validation errors', model: DiffNote
+
+ context do
+ let(:diff_refs) { build(:merge_request).diff_refs } # Allow fake diff refs so arguments are valid
+
+ it_behaves_like 'a Note mutation when the given resource id is not for a Noteable'
+ end
+
+ it 'returns the note with the correct position' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq('Body text')
+ mutation_position_response = mutation_response['note']['position']
+ expect(mutation_position_response['filePath']).to eq('files/images/any_image2.png')
+ expect(mutation_position_response['oldPath']).to eq('files/images/any_image.png')
+ expect(mutation_position_response['newPath']).to eq('files/images/any_image2.png')
+ expect(mutation_position_response['positionType']).to eq('image')
+ expect(mutation_position_response['width']).to eq(100)
+ expect(mutation_position_response['height']).to eq(200)
+ expect(mutation_position_response['x']).to eq(1)
+ expect(mutation_position_response['y']).to eq(2)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
new file mode 100644
index 00000000000..14aaa430ac9
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Adding a Note' do
+ include GraphqlHelpers
+
+ set(:current_user) { create(:user) }
+ let(:noteable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:project) { create(:project) }
+ let(:discussion) { nil }
+ let(:mutation) do
+ variables = {
+ noteable_id: GitlabSchema.id_from_object(noteable).to_s,
+ discussion_id: (GitlabSchema.id_from_object(discussion).to_s if discussion),
+ body: 'Body text'
+ }
+
+ graphql_mutation(:create_note, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:create_note)
+ end
+
+ it_behaves_like 'a Note mutation when the user does not have permission'
+
+ context 'when the user has permission' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'a Note mutation that creates a Note'
+
+ it_behaves_like 'a Note mutation when there are active record validation errors'
+
+ it_behaves_like 'a Note mutation when the given resource id is not for a Noteable'
+
+ it 'returns the note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq('Body text')
+ end
+
+ describe 'creating Notes in reply to a discussion' do
+ context 'when the user does not have permission to create notes on the discussion' do
+ let(:discussion) { create(:discussion_note).to_discussion }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ["The discussion does not exist or you don't have permission to perform this action"]
+ end
+
+ context 'when the user has permission to create notes on the discussion' do
+ let(:discussion) { create(:discussion_note, project: project).to_discussion }
+
+ it 'creates a Note in a discussion' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['discussion']['id']).to eq(discussion.to_global_id.to_s)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
new file mode 100644
index 00000000000..337a6e6f6e6
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Destroying a Note' do
+ include GraphqlHelpers
+
+ let!(:note) { create(:note) }
+ let(:mutation) do
+ variables = {
+ id: GitlabSchema.id_from_object(note).to_s
+ }
+
+ graphql_mutation(:destroy_note, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:destroy_note)
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
+
+ it 'does not destroy the Note' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.not_to change { Note.count }
+ end
+ end
+
+ context 'when the user has permission' do
+ let(:current_user) { note.author }
+
+ it_behaves_like 'a Note mutation when the given resource id is not for a Note'
+
+ it 'destroys the Note' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change { Note.count }.by(-1)
+ end
+
+ it 'returns an empty Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to have_key('note')
+ expect(mutation_response['note']).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/notes/update_spec.rb b/spec/requests/api/graphql/mutations/notes/update_spec.rb
new file mode 100644
index 00000000000..958f640995a
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/update_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Updating a Note' do
+ include GraphqlHelpers
+
+ let!(:note) { create(:note, note: original_body) }
+ let(:original_body) { 'Initial body text' }
+ let(:updated_body) { 'Updated body text' }
+ let(:mutation) do
+ variables = {
+ id: GitlabSchema.id_from_object(note).to_s,
+ body: updated_body
+ }
+
+ graphql_mutation(:update_note, variables)
+ end
+
+ def mutation_response
+ graphql_mutation_response(:update_note)
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
+
+ it 'does not update the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(original_body)
+ end
+ end
+
+ context 'when the user has permission' do
+ let(:current_user) { note.author }
+
+ it_behaves_like 'a Note mutation when the given resource id is not for a Note'
+
+ it 'updates the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(updated_body)
+ end
+
+ it 'returns the updated Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(updated_body)
+ end
+
+ context 'when there are ActiveRecord validation errors' do
+ let(:updated_body) { '' }
+
+ it_behaves_like 'a mutation that returns errors in the response', errors: ["Note can't be blank"]
+
+ it 'does not update the Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(note.reload.note).to eq(original_body)
+ end
+
+ it 'returns the Note with its original body' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['body']).to eq(original_body)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
index 67371cb35b6..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
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index c67412a44c1..a2aae257352 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1102,6 +1102,12 @@ describe API::Projects do
expect(json_response['wiki_enabled']).to be_present
expect(json_response['jobs_enabled']).to be_present
expect(json_response['snippets_enabled']).to be_present
+ expect(json_response['snippets_access_level']).to be_present
+ expect(json_response['repository_access_level']).to be_present
+ expect(json_response['issues_access_level']).to be_present
+ expect(json_response['merge_requests_access_level']).to be_present
+ expect(json_response['wiki_access_level']).to be_present
+ expect(json_response['builds_access_level']).to be_present
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
expect(json_response['container_registry_enabled']).to be_present
expect(json_response['created_at']).to be_present
@@ -1913,6 +1919,34 @@ describe API::Projects do
end
end
+ it 'updates builds_access_level' do
+ project_param = { builds_access_level: 'private' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['builds_access_level']).to eq('private')
+ end
+
+ it 'updates build_git_strategy' do
+ project_param = { build_git_strategy: 'clone' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['build_git_strategy']).to eq('clone')
+ end
+
+ it 'rejects to update build_git_strategy when build_git_strategy is invalid' do
+ project_param = { build_git_strategy: 'invalid' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
it 'updates merge_method' do
project_param = { merge_method: 'ff' }
@@ -1946,6 +1980,26 @@ describe API::Projects do
'-/system/project/avatar/'\
"#{project3.id}/banana_sample.gif")
end
+
+ it 'updates auto_devops_deploy_strategy' do
+ project_param = { auto_devops_deploy_strategy: 'timed_incremental' }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['auto_devops_deploy_strategy']).to eq('timed_incremental')
+ end
+
+ it 'updates auto_devops_enabled' do
+ project_param = { auto_devops_enabled: false }
+
+ put api("/projects/#{project3.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(json_response['auto_devops_enabled']).to eq(false)
+ end
end
context 'when authenticated as project maintainer' 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/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_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index aa759ac9edc..22578436c18 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -214,6 +214,19 @@ describe MergeRequests::MergeService do
allow(Rails.logger).to receive(:error)
end
+ context 'when source is missing' do
+ it 'logs and saves error' do
+ allow(merge_request).to receive(:diff_head_sha) { nil }
+
+ error_message = 'No source for merge'
+
+ service.execute(merge_request)
+
+ expect(merge_request.merge_error).to eq(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
+ end
+ end
+
it 'logs and saves error if there is an exception' do
error_message = 'error message'
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 e2f201677fa..758679edc45 100644
--- a/spec/services/merge_requests/merge_to_ref_service_spec.rb
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -191,6 +191,28 @@ describe MergeRequests::MergeToRefService do
it { expect(todo).not_to be_done }
end
+ context 'when source is missing' do
+ it 'returns error' do
+ allow(merge_request).to receive(:diff_head_sha) { nil }
+
+ error_message = 'No source for merge'
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
+ 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 } }
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/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 1a09d48f4cd..ec3c460cd37 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -57,7 +57,8 @@ module GraphqlHelpers
end
def variables_for_mutation(name, input)
- graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h
+ graphql_input = prepare_input_for_mutation(input)
+
result = { input_variable_name_for_mutation(name) => graphql_input }
# Avoid trying to serialize multipart data into JSON
@@ -68,6 +69,18 @@ module GraphqlHelpers
end
end
+ # Recursively convert a Hash with Ruby-style keys to GraphQL fieldname-style keys
+ #
+ # prepare_input_for_mutation({ 'my_key' => 1 })
+ # => { 'myKey' => 1}
+ def prepare_input_for_mutation(input)
+ input.map do |name, value|
+ value = prepare_input_for_mutation(value) if value.is_a?(Hash)
+
+ [GraphqlHelpers.fieldnamerize(name), value]
+ end.to_h
+ end
+
def input_variable_name_for_mutation(mutation_name)
mutation_name = GraphqlHelpers.fieldnamerize(mutation_name)
mutation_field = GitlabSchema.mutation.fields[mutation_name]
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/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
new file mode 100644
index 00000000000..f2e1a95345b
--- /dev/null
+++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a Note mutation that does not create a Note' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.not_to change { Note.count }
+ end
+end
+
+RSpec.shared_examples 'a Note mutation that creates a Note' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change { Note.count }.by(1)
+ end
+end
+
+RSpec.shared_examples 'a Note mutation when the user does not have permission' do
+ it_behaves_like 'a Note mutation that does not create a Note'
+
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action']
+end
+
+RSpec.shared_examples 'a Note mutation when there are active record validation errors' do |model: Note|
+ before do
+ expect_next_instance_of(model) do |note|
+ expect(note).to receive(:valid?).at_least(:once).and_return(false)
+ expect(note).to receive_message_chain(
+ :errors,
+ :full_messages
+ ).and_return(['Error 1', 'Error 2'])
+ end
+ end
+
+ it_behaves_like 'a Note mutation that does not create a Note'
+
+ it_behaves_like 'a mutation that returns errors in the response', errors: ['Error 1', 'Error 2']
+
+ it 'returns an empty Note' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response).to have_key('note')
+ expect(mutation_response['note']).to be_nil
+ end
+end
+
+RSpec.shared_examples 'a Note mutation when the given resource id is not for a Noteable' do
+ let(:noteable) { create(:label, project: project) }
+
+ it_behaves_like 'a Note mutation that does not create a Note'
+
+ it_behaves_like 'a mutation that returns top-level errors', errors: ['Cannot add notes to this resource']
+end
+
+RSpec.shared_examples 'a Note mutation when the given resource id is not for a Note' do
+ let(:note) { create(:issue) }
+
+ it_behaves_like 'a mutation that returns top-level errors', errors: ['Resource is not a note']
+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 825a1785c3e..dc5e0662396 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -705,10 +705,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579"
integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA==
-"@gitlab/ui@^5.3.2":
- version "5.3.2"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.3.2.tgz#8ee906cf0586834de0077e165f25764c0bf8a9e9"
- integrity sha512-VgxlDXqG2q+u72Km+/Ljdvjh0DzvljvsztiXTxnOO+Eb+/I26JBWfdboqFr3E02JzT8W4s4rRinhRttLWfcM/A==
+"@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"