summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml2
-rw-r--r--.mdlrc.style4
-rw-r--r--CHANGELOG.md20
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock16
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js2
-rw-r--r--app/assets/javascripts/diffs/components/diff_expansion_cell.vue246
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_gutter_content.vue106
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue53
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_table_row.vue5
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue18
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue56
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue11
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue17
-rw-r--r--app/assets/javascripts/diffs/store/actions.js6
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js27
-rw-r--r--app/assets/javascripts/diffs/store/utils.js26
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql4
-rw-r--r--app/assets/javascripts/ide/components/branches/search_list.vue26
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue28
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue5
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js23
-rw-r--r--app/assets/javascripts/mr_notes/stores/index.js2
-rw-r--r--app/assets/javascripts/mr_notes/stores/modules/index.js4
-rw-r--r--app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue47
-rw-r--r--app/assets/javascripts/notes/stores/getters.js9
-rw-r--r--app/assets/javascripts/pages/search/show/refresh_counts.js24
-rw-r--r--app/assets/javascripts/pages/search/show/search.js2
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.query.graphql7
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/highlight/white_base.scss20
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss3
-rw-r--r--app/assets/stylesheets/pages/builds.scss20
-rw-r--r--app/assets/stylesheets/pages/diff.scss8
-rw-r--r--app/assets/stylesheets/pages/members.scss2
-rw-r--r--app/assets/stylesheets/pages/todos.scss2
-rw-r--r--app/controllers/groups/group_members_controller.rb13
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb8
-rw-r--r--app/controllers/search_controller.rb9
-rw-r--r--app/helpers/search_helper.rb34
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/member.rb4
-rw-r--r--app/models/user.rb10
-rw-r--r--app/presenters/blobs/unfold_presenter.rb40
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/views/groups/group_members/index.html.haml78
-rw-r--r--app/views/help/_shortcuts.html.haml160
-rw-r--r--app/views/import/bitbucket_server/status.html.haml11
-rw-r--r--app/views/layouts/_search.html.haml1
-rw-r--r--app/views/projects/issues/_issue.html.haml5
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml7
-rw-r--r--app/views/search/_category.html.haml84
-rw-r--r--app/views/shared/members/_filter_2fa_dropdown.html.haml4
-rw-r--r--app/views/shared/members/_sort_dropdown.html.haml3
-rw-r--r--changelogs/unreleased/44036-someone-edited-the-issue-at-the-same-time-is-regularly-seen-despite-that-being-a-filthy-lie.yml5
-rw-r--r--changelogs/unreleased/58035-expand-mr-diff.yml5
-rw-r--r--changelogs/unreleased/59590-keyboard-shortcut-for-jump-to-next-unresolved-discussion.yml5
-rw-r--r--changelogs/unreleased/61332-web-ide-mr-branch-dropdown-closes-unexpectedly.yml5
-rw-r--r--changelogs/unreleased/63942-remove-config-action_dispatch-use_authenticated_cookie_encryption-configuration.yml5
-rw-r--r--changelogs/unreleased/65278-fix-puma-master-counter-wipe.yml5
-rw-r--r--changelogs/unreleased/65530-add-externalization-and-fix-regression-in-shortcuts-helper-modal.yml6
-rw-r--r--changelogs/unreleased/65790-highlight.yml5
-rw-r--r--changelogs/unreleased/alipniagov-fix-wiki_can_not_be_created_total-counter.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-51260-add-filtering-to-bitbucket-server-import.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-63408-user-mapping.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.59.0.yml5
-rw-r--r--changelogs/unreleased/improve-quick-action-messages.yml5
-rw-r--r--changelogs/unreleased/load-search-counts-async.yml5
-rw-r--r--changelogs/unreleased/sh-break-out-invited-group-members.yml5
-rw-r--r--changelogs/unreleased/sh-fix-import-export-suggestions.yml5
-rw-r--r--changelogs/unreleased/update-graphicsmagick-to-1-3-33.yml5
-rw-r--r--config.ru18
-rw-r--r--config/application.rb5
-rw-r--r--config/initializers/7_prometheus_metrics.rb19
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20190612111201_add_geo_container_repository_counters.rb16
-rw-r--r--db/schema.rb4
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/auditor_users.md2
-rw-r--r--doc/administration/auth/oidc.md65
-rw-r--r--doc/administration/container_registry.md32
-rw-r--r--doc/administration/geo/replication/index.md17
-rw-r--r--doc/administration/high_availability/pgbouncer.md127
-rw-r--r--doc/administration/index.md7
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md30
-rw-r--r--doc/administration/operations/unicorn.md2
-rw-r--r--doc/administration/pages/source.md45
-rw-r--r--doc/administration/raketasks/storage.md8
-rw-r--r--doc/api/README.md4
-rw-r--r--doc/api/geo_nodes.md12
-rw-r--r--doc/ci/caching/index.md6
-rw-r--r--doc/ci/chatops/README.md10
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md2
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md36
-rw-r--r--doc/ci/environments.md14
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md2
-rw-r--r--doc/ci/examples/code_quality.md2
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md8
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md2
-rw-r--r--doc/ci/jenkins/index.md28
-rw-r--r--doc/ci/review_apps/index.md4
-rw-r--r--doc/ci/runners/README.md12
-rw-r--r--doc/ci/variables/predefined_variables.md2
-rw-r--r--doc/development/README.md2
-rw-r--r--doc/development/contributing/merge_request_workflow.md31
-rw-r--r--doc/development/documentation/index.md6
-rw-r--r--doc/development/documentation/styleguide.md118
-rw-r--r--doc/development/fe_guide/event_tracking.md8
-rw-r--r--doc/development/filtering_by_label.md6
-rw-r--r--doc/development/testing_guide/end_to_end/index.md1
-rw-r--r--doc/development/testing_guide/end_to_end/page_objects.md5
-rw-r--r--doc/install/requirements.md5
-rw-r--r--doc/integration/facebook.md6
-rw-r--r--doc/integration/jira_development_panel.md73
-rw-r--r--doc/integration/kerberos.md10
-rw-r--r--doc/subscriptions/index.md2
-rw-r--r--doc/tools/email.md4
-rw-r--r--doc/topics/autodevops/quick_start_guide.md18
-rw-r--r--doc/topics/git/partial_clone.md60
-rw-r--r--doc/update/README.md24
-rw-r--r--doc/update/upgrading_from_source.md14
-rw-r--r--doc/user/admin_area/license.md12
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md6
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--doc/user/discussions/index.md27
-rw-r--r--doc/user/group/contribution_analytics/index.md3
-rw-r--r--doc/user/group/index.md22
-rw-r--r--doc/user/group/saml_sso/scim_setup.md6
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/project/file_lock.md2
-rw-r--r--doc/user/project/import/bitbucket.md18
-rw-r--r--doc/user/project/import/bitbucket_server.md12
-rw-r--r--doc/user/project/import/gemnasium.md42
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_select_project.pngbin19427 -> 0 bytes
-rw-r--r--doc/user/project/import/img/bitbucket_server_import_select_project_v12_3.pngbin0 -> 47059 bytes
-rw-r--r--doc/user/project/import/index.md2
-rw-r--r--doc/user/project/import/manifest.md5
-rw-r--r--doc/user/project/import/tfs.md45
-rw-r--r--doc/user/project/import/tfvc.md46
-rw-r--r--doc/user/project/integrations/jira_cloud_configuration.md2
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md46
-rw-r--r--doc/user/project/integrations/redmine.md18
-rw-r--r--doc/user/project/members/share_project_with_groups.md6
-rw-r--r--doc/user/project/merge_requests/code_quality.md1
-rw-r--r--doc/user/project/merge_requests/code_quality_diff.md1
-rw-r--r--doc/user/project/merge_requests/merge_request_approvals.md21
-rw-r--r--doc/user/project/merge_requests/merge_request_dependencies.md13
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md17
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md2
-rw-r--r--doc/user/project/pages/getting_started_part_two.md44
-rw-r--r--doc/user/project/pages/index.md2
-rw-r--r--doc/user/project/protected_branches.md6
-rw-r--r--doc/user/project/protected_tags.md8
-rw-r--r--doc/user/project/quick_actions.md4
-rw-r--r--doc/user/project/repository/web_editor.md2
-rw-r--r--doc/workflow/shortcuts.md2
-rw-r--r--lib/bitbucket/representation/comment.rb2
-rw-r--r--lib/bitbucket/representation/issue.rb2
-rw-r--r--lib/bitbucket/representation/pull_request.rb2
-rw-r--r--lib/bitbucket_server/client.rb3
-rw-r--r--lib/feature/gitaly.rb2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb8
-rw-r--r--lib/gitlab/ci/ansi2html.rb2
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_observer.rb4
-rw-r--r--lib/gitlab/gitaly_client/notification_service.rb22
-rw-r--r--lib/gitlab/import_export/import_export.yml2
-rw-r--r--lib/gitlab/project_search_results.rb15
-rw-r--r--lib/gitlab/quick_actions/issuable_actions.rb12
-rw-r--r--lib/gitlab/quick_actions/issue_actions.rb31
-rw-r--r--lib/gitlab/quick_actions/issue_and_merge_request_actions.rb10
-rw-r--r--lib/gitlab/search_results.rb23
-rw-r--r--lib/gitlab/snippet_search_results.rb11
-rw-r--r--locale/gitlab.pot269
-rw-r--r--package.json2
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb3
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb33
-rw-r--r--spec/controllers/import/bitbucket_server_controller_spec.rb14
-rw-r--r--spec/controllers/search_controller_spec.rb202
-rw-r--r--spec/factories/group_members.rb4
-rw-r--r--spec/features/groups/members/filter_members_spec.rb6
-rw-r--r--spec/features/groups/members/manage_members_spec.rb4
-rw-r--r--spec/features/groups/members/sort_members_spec.rb18
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb12
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb18
-rw-r--r--spec/features/projects/members/sorting_spec.rb18
-rw-r--r--spec/features/search/user_searches_for_users_spec.rb4
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb17
-rw-r--r--spec/frontend/notes/components/discussion_keyboard_navigator_spec.js77
-rw-r--r--spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap7
-rw-r--r--spec/frontend/pages/search/show/refresh_counts_spec.js35
-rw-r--r--spec/helpers/search_helper_spec.rb44
-rw-r--r--spec/javascripts/diffs/components/compare_versions_spec.js4
-rw-r--r--spec/javascripts/diffs/components/diff_expansion_cell_spec.js64
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js20
-rw-r--r--spec/javascripts/diffs/components/diff_line_gutter_content_spec.js15
-rw-r--r--spec/javascripts/diffs/components/diff_line_note_form_spec.js4
-rw-r--r--spec/javascripts/diffs/components/diff_table_cell_spec.js4
-rw-r--r--spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js31
-rw-r--r--spec/javascripts/diffs/components/inline_diff_table_row_spec.js4
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js8
-rw-r--r--spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js31
-rw-r--r--spec/javascripts/diffs/components/parallel_diff_view_spec.js4
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js6
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js5
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js11
-rw-r--r--spec/javascripts/issue_show/components/form_spec.js111
-rw-r--r--spec/javascripts/jobs/components/job_log_spec.js4
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js48
-rw-r--r--spec/lib/bitbucket/representation/comment_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/issue_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/pull_request_spec.rb2
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb11
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/ansi2html_spec.rb2
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/notification_service_spec.rb17
-rw-r--r--spec/lib/gitlab/import_export/project.json16
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb7
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb22
-rw-r--r--spec/lib/gitlab/search_results_spec.rb37
-rw-r--r--spec/lib/gitlab/snippet_search_results_spec.rb18
-rw-r--r--spec/models/member_spec.rb13
-rw-r--r--spec/presenters/blobs/unfold_presenter_spec.rb68
-rw-r--r--spec/services/projects/update_service_spec.rb5
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb36
-rw-r--r--spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb2
-rw-r--r--yarn.lock8
234 files changed, 3096 insertions, 1350 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9531d10be05..f926cbc2939 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.33"
variables:
RAILS_ENV: "test"
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 50f5b58d4e7..5c3278fcf53 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -15,7 +15,7 @@
.gitlab:assets:compile-metadata:
<<: *assets-compile-cache
extends: .dedicated-no-docs-pull-cache-job
- image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.22-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.29-docker-18.06.1
+ image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.22-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-18.06.1
dependencies:
- setup-test-env
services:
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index aeae5bd26e9..50476b43dd6 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -90,7 +90,7 @@
.rspec-metadata-pg-10: &rspec-metadata-pg-10
<<: *rspec-metadata
<<: *use-pg-10
- image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.29"
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
# DB migration, rollback, and seed jobs
.db-migrate-reset: &db-migrate-reset
diff --git a/.mdlrc.style b/.mdlrc.style
index 36fbba3543b..b315c99e3fc 100644
--- a/.mdlrc.style
+++ b/.mdlrc.style
@@ -8,12 +8,16 @@ rule "MD001"
rule "MD002"
rule "MD003", :style => :atx
rule "MD006"
+rule "MD010"
rule "MD011"
+rule "MD012"
rule "MD019"
rule "MD022"
rule "MD023"
rule "MD025"
rule "MD028"
+rule "MD029", :style => :one
+rule "MD030"
rule "MD032"
rule "MD034"
rule "MD037"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0752708d5e8..267a1caafec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 12.1.5
+
+### Security (2 changes)
+
+- Upgrade Gitaly to 1.53.2 to prevent revision flag injection exploits.
+- Upgrade pages to 1.7.1 to prevent gitlab api token recovery from cookie.
+
+
## 12.1.4
### Fixed (3 changes, 1 of them is from the community)
@@ -330,6 +338,10 @@ entry.
- Removes EE differences for app/views/admin/users/show.html.haml.
+## 12.0.6
+
+- No changes.
+
## 12.0.3 (2019-06-27)
- No changes.
@@ -678,6 +690,14 @@ entry.
- Moves snowplow to CE repo.
+## 11.11.8
+
+### Security (2 changes)
+
+- Upgrade Gitaly to 1.42.7 to prevent revision flag injection exploits.
+- Upgrade pages to 1.5.1 to prevent gitlab api token recovery from cookie.
+
+
## 11.11.7
### Security (9 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 373aea97570..bb120e876c6 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.57.0
+1.59.0
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index bd8bf882d06..943f9cbc4ec 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.7.0
+1.7.1
diff --git a/Gemfile b/Gemfile
index 22746f9c5ae..55143693d5c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -137,7 +137,7 @@ gem 'asciidoctor-plantuml', '0.0.9'
gem 'rouge', '~> 3.7'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
-gem 'nokogiri', '~> 1.10.3'
+gem 'nokogiri', '~> 1.10.4'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
@@ -283,7 +283,7 @@ gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.9.7'
# LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '~> 0.3.0'
+gem 'gitlab-labkit', '~> 0.4.2'
# I18n
gem 'ruby_parser', '~> 3.8', require: false
@@ -424,8 +424,8 @@ group :ed25519 do
gem 'bcrypt_pbkdf', '~> 1.0'
end
-# Gitaly GRPC client
-gem 'gitaly-proto', '~> 1.37.0', require: 'gitaly'
+# Gitaly GRPC protocol definitions
+gem 'gitaly', '~> 1.58.0'
gem 'grpc', '~> 1.19.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 80f36a9457c..6aa96d54abb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -236,7 +236,7 @@ GEM
fast_blank (1.0.0)
fast_gettext (1.6.0)
ffaker (2.10.0)
- ffi (1.10.0)
+ ffi (1.11.1)
flipper (0.13.0)
flipper-active_record (0.13.0)
activerecord (>= 3.2, < 6)
@@ -309,13 +309,13 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (1.37.0)
+ gitaly (1.58.0)
grpc (~> 1.0)
github-markup (1.7.0)
- gitlab-labkit (0.3.0)
+ gitlab-labkit (0.4.2)
actionpack (~> 5)
activesupport (~> 5)
- grpc (~> 1.19.0)
+ grpc (~> 1.19)
jaeger-client (~> 0.10)
opentracing (~> 0.4)
gitlab-markup (1.7.0)
@@ -541,7 +541,7 @@ GEM
net-ssh (5.2.0)
netrc (0.11.0)
nio4r (2.3.1)
- nokogiri (1.10.3)
+ nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
nokogumbo (1.5.0)
nokogiri
@@ -1096,9 +1096,9 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 1.37.0)
+ gitaly (~> 1.58.0)
github-markup (~> 1.7.0)
- gitlab-labkit (~> 0.3.0)
+ gitlab-labkit (~> 0.4.2)
gitlab-markup (~> 1.7.0)
gitlab-sidekiq-fetcher (= 0.5.1)
gitlab-styles (~> 2.7)
@@ -1148,7 +1148,7 @@ DEPENDENCIES
nakayoshi_fork (~> 0.0.4)
net-ldap
net-ssh (~> 5.2)
- nokogiri (~> 1.10.3)
+ nokogiri (~> 1.10.4)
oauth2 (~> 1.4)
octokit (~> 4.9)
omniauth (~> 1.8)
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
index 8b7e6a56d25..208c91a1f08 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
@@ -6,6 +6,8 @@ export default class ShortcutsWiki extends ShortcutsNavigation {
constructor() {
super();
Mousetrap.bind('e', ShortcutsWiki.editWiki);
+
+ this.enabledHelp.push('.hidden-shortcut.wiki');
}
static editWiki() {
diff --git a/app/assets/javascripts/diffs/components/diff_expansion_cell.vue b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
new file mode 100644
index 00000000000..925385fa98a
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/diff_expansion_cell.vue
@@ -0,0 +1,246 @@
+<script>
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
+import { mapState, mapActions } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
+import { UNFOLD_COUNT } from '../constants';
+import * as utils from '../store/utils';
+import tooltip from '../../vue_shared/directives/tooltip';
+
+const EXPAND_ALL = 0;
+const EXPAND_UP = 1;
+const EXPAND_DOWN = 2;
+
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ },
+ props: {
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
+ required: true,
+ },
+ line: {
+ type: Object,
+ required: true,
+ },
+ isTop: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isBottom: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ colspan: {
+ type: Number,
+ required: false,
+ default: 3,
+ },
+ },
+ computed: {
+ ...mapState({
+ diffViewType: state => state.diffs.diffViewType,
+ diffFiles: state => state.diffs.diffFiles,
+ }),
+ canExpandUp() {
+ return !this.isBottom;
+ },
+ canExpandDown() {
+ return this.isBottom || !this.isTop;
+ },
+ },
+ created() {
+ this.EXPAND_DOWN = EXPAND_DOWN;
+ this.EXPAND_UP = EXPAND_UP;
+ },
+ methods: {
+ ...mapActions('diffs', ['loadMoreLines']),
+ getPrevLineNumber(oldLineNumber, newLineNumber) {
+ const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash);
+ const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, {
+ oldLineNumber,
+ newLineNumber,
+ });
+ const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2];
+ return (prevLine && prevLine.new_line) || 0;
+ },
+ callLoadMoreLines(
+ endpoint,
+ params,
+ lineNumbers,
+ fileHash,
+ isExpandDown = false,
+ nextLineNumbers = {},
+ ) {
+ this.loadMoreLines({ endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers })
+ .then(() => {
+ this.isRequesting = false;
+ })
+ .catch(() => {
+ createFlash(s__('Diffs|Something went wrong while fetching diff lines.'));
+ this.isRequesting = false;
+ });
+ },
+ handleExpandLines(type = EXPAND_ALL) {
+ if (this.isRequesting) {
+ return;
+ }
+
+ this.isRequesting = true;
+ const endpoint = this.contextLinesPath;
+ const { fileHash } = this;
+ const view = this.diffViewType;
+ const oldLineNumber = this.line.meta_data.old_pos || 0;
+ const newLineNumber = this.line.meta_data.new_pos || 0;
+ const offset = newLineNumber - oldLineNumber;
+
+ const expandOptions = { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset };
+
+ if (type === EXPAND_UP) {
+ this.handleExpandUpLines(expandOptions);
+ } else if (type === EXPAND_DOWN) {
+ this.handleExpandDownLines(expandOptions);
+ } else {
+ this.handleExpandAllLines(expandOptions);
+ }
+ },
+ handleExpandUpLines(expandOptions = EXPAND_ALL) {
+ const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions;
+
+ const bottom = this.isBottom;
+ const lineNumber = newLineNumber - 1;
+ const to = lineNumber;
+ let since = lineNumber - UNFOLD_COUNT;
+ let unfold = true;
+
+ const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);
+ if (since <= prevLineNumber + 1) {
+ since = prevLineNumber + 1;
+ unfold = false;
+ }
+
+ const params = { since, to, bottom, offset, unfold, view };
+ const lineNumbers = { oldLineNumber, newLineNumber };
+ this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash);
+ },
+ handleExpandDownLines(expandOptions) {
+ const {
+ endpoint,
+ fileHash,
+ view,
+ oldLineNumber: metaOldPos,
+ newLineNumber: metaNewPos,
+ offset,
+ } = expandOptions;
+
+ const bottom = true;
+ const nextLineNumbers = {
+ old_line: metaOldPos,
+ new_line: metaNewPos,
+ };
+
+ let unfold = true;
+ let isExpandDown = false;
+ let oldLineNumber = metaOldPos;
+ let newLineNumber = metaNewPos;
+ let lineNumber = metaNewPos + 1;
+ let since = lineNumber;
+ let to = lineNumber + UNFOLD_COUNT;
+
+ if (!this.isBottom) {
+ const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);
+
+ isExpandDown = true;
+ oldLineNumber = prevLineNumber - offset;
+ newLineNumber = prevLineNumber;
+ lineNumber = prevLineNumber + 1;
+ since = lineNumber;
+ to = lineNumber + UNFOLD_COUNT;
+
+ if (to >= metaNewPos) {
+ to = metaNewPos - 1;
+ unfold = false;
+ }
+ }
+
+ const params = { since, to, bottom, offset, unfold, view };
+ const lineNumbers = { oldLineNumber, newLineNumber };
+ this.callLoadMoreLines(
+ endpoint,
+ params,
+ lineNumbers,
+ fileHash,
+ isExpandDown,
+ nextLineNumbers,
+ );
+ },
+ handleExpandAllLines(expandOptions) {
+ const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions;
+ const bottom = this.isBottom;
+ const unfold = false;
+ let since;
+ let to;
+
+ if (this.isTop) {
+ since = 1;
+ to = newLineNumber - 1;
+ } else if (bottom) {
+ since = newLineNumber + 1;
+ to = -1;
+ } else {
+ const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);
+ since = prevLineNumber + 1;
+ to = newLineNumber - 1;
+ }
+
+ const params = { since, to, bottom, offset, unfold, view };
+ const lineNumbers = { oldLineNumber, newLineNumber };
+ this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash);
+ },
+ },
+};
+</script>
+
+<template>
+ <td :colspan="colspan">
+ <div class="content js-line-expansion-content">
+ <a
+ v-if="canExpandUp"
+ v-tooltip
+ class="cursor-pointer js-unfold unfold-icon"
+ data-placement="top"
+ data-container="body"
+ :title="__('Expand up')"
+ @click="handleExpandLines(EXPAND_UP)"
+ >
+ <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 -->
+ <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(270deg);" />
+ </a>
+ <a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
+ <span>{{ s__('Diffs|Show all lines') }}</span>
+ </a>
+ <a
+ v-if="canExpandDown"
+ v-tooltip
+ class="cursor-pointer js-unfold-down has-tooltip unfold-icon"
+ data-placement="top"
+ data-container="body"
+ :title="__('Expand down')"
+ @click="handleExpandLines(EXPAND_DOWN)"
+ >
+ <!-- TODO: remove style & replace with correct icon, waiting for MR https://gitlab.com/gitlab-org/gitlab-design/issues/499 -->
+ <icon :size="12" name="expand-left" aria-hidden="true" style="transform: rotate(90deg);" />
+ </a>
+ </div>
+ </td>
+</template>
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index 351110f0a87..434d554d148 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -1,11 +1,8 @@
<script>
-import createFlash from '~/flash';
-import { s__ } from '~/locale';
import { mapState, mapGetters, mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
-import { LINE_POSITION_RIGHT, UNFOLD_COUNT } from '../constants';
-import * as utils from '../store/utils';
+import { LINE_POSITION_RIGHT } from '../constants';
export default {
components: {
@@ -115,89 +112,36 @@ export default {
handleCommentButton() {
this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash });
},
- handleLoadMoreLines() {
- if (this.isRequesting) {
- return;
- }
-
- this.isRequesting = true;
- const endpoint = this.contextLinesPath;
- const oldLineNumber = this.line.meta_data.old_pos || 0;
- const newLineNumber = this.line.meta_data.new_pos || 0;
- const offset = newLineNumber - oldLineNumber;
- const bottom = this.isBottom;
- const { fileHash } = this;
- const view = this.diffViewType;
- let unfold = true;
- let lineNumber = newLineNumber - 1;
- let since = lineNumber - UNFOLD_COUNT;
- let to = lineNumber;
-
- if (bottom) {
- lineNumber = newLineNumber + 1;
- since = lineNumber;
- to = lineNumber + UNFOLD_COUNT;
- } else {
- const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash);
- const indexForInline = utils.findIndexInInlineLines(diffFile.highlighted_diff_lines, {
- oldLineNumber,
- newLineNumber,
- });
- const prevLine = diffFile.highlighted_diff_lines[indexForInline - 2];
- const prevLineNumber = (prevLine && prevLine.new_line) || 0;
-
- if (since <= prevLineNumber + 1) {
- since = prevLineNumber + 1;
- unfold = false;
- }
- }
-
- const params = { since, to, bottom, offset, unfold, view };
- const lineNumbers = { oldLineNumber, newLineNumber };
- this.loadMoreLines({ endpoint, params, lineNumbers, fileHash })
- .then(() => {
- this.isRequesting = false;
- })
- .catch(() => {
- createFlash(s__('Diffs|Something went wrong while fetching diff lines.'));
- this.isRequesting = false;
- });
- },
},
};
</script>
<template>
<div>
- <span v-if="isMatchLine" class="context-cell" role="button" @click="handleLoadMoreLines"
- >...</span
+ <button
+ v-if="shouldRenderCommentButton"
+ v-show="shouldShowCommentButton"
+ type="button"
+ class="add-diff-note js-add-diff-note-button qa-diff-comment"
+ title="Add a comment to this line"
+ @click="handleCommentButton"
+ >
+ <icon :size="12" name="comment" />
+ </button>
+ <a
+ v-if="lineNumber"
+ :data-linenumber="lineNumber"
+ :href="lineHref"
+ @click="setHighlightedRow(lineCode)"
>
- <template v-else>
- <button
- v-if="shouldRenderCommentButton"
- v-show="shouldShowCommentButton"
- type="button"
- class="add-diff-note js-add-diff-note-button qa-diff-comment"
- title="Add a comment to this line"
- @click="handleCommentButton"
- >
- <icon :size="12" name="comment" />
- </button>
- <a
- v-if="lineNumber"
- :data-linenumber="lineNumber"
- :href="lineHref"
- @click="setHighlightedRow(lineCode)"
- >
- </a>
- <diff-gutter-avatars
- v-if="shouldShowAvatarsOnGutter"
- :discussions="line.discussions"
- :discussions-expanded="line.discussionsExpanded"
- @toggleLineDiscussions="
- toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded })
- "
- />
- </template>
+ </a>
+ <diff-gutter-avatars
+ v-if="shouldShowAvatarsOnGutter"
+ :discussions="line.discussions"
+ :discussions-expanded="line.discussionsExpanded"
+ @toggleLineDiscussions="
+ toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded })
+ "
+ />
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue b/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue
new file mode 100644
index 00000000000..6e732727f42
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/inline_diff_expansion_row.vue
@@ -0,0 +1,53 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import DiffExpansionCell from './diff_expansion_cell.vue';
+import { MATCH_LINE_TYPE } from '../constants';
+
+export default {
+ components: {
+ Icon,
+ DiffExpansionCell,
+ },
+ props: {
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
+ required: true,
+ },
+ line: {
+ type: Object,
+ required: true,
+ },
+ isTop: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isBottom: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ isMatchLine() {
+ return this.line.type === MATCH_LINE_TYPE;
+ },
+ },
+};
+</script>
+
+<template>
+ <tr v-if="isMatchLine" class="line_expansion match">
+ <diff-expansion-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line"
+ :is-top="isTop"
+ :is-bottom="isBottom"
+ />
+ </tr>
+</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 2d5262baeec..55a8df43c62 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -2,6 +2,7 @@
import { mapActions, mapState } from 'vuex';
import DiffTableCell from './diff_table_cell.vue';
import {
+ MATCH_LINE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
CONTEXT_LINE_TYPE,
@@ -58,6 +59,9 @@ export default {
inlineRowId() {
return this.line.line_code || `${this.fileHash}_${this.line.old_line}_${this.line.new_line}`;
},
+ isMatchLine() {
+ return this.line.type === MATCH_LINE_TYPE;
+ },
},
created() {
this.newLineType = NEW_LINE_TYPE;
@@ -81,6 +85,7 @@ export default {
<template>
<tr
+ v-if="!isMatchLine"
:id="inlineRowId"
:class="classNameMap"
class="line_holder"
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index b2bc3d9914f..aee01409db7 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -3,6 +3,7 @@ import { mapGetters } from 'vuex';
import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
+import inlineDiffExpansionRow from './inline_diff_expansion_row.vue';
export default {
components: {
@@ -10,6 +11,7 @@ export default {
inlineDiffTableRow,
InlineDraftCommentRow: () =>
import('ee_component/batch_comments/components/inline_draft_comment_row.vue'),
+ inlineDiffExpansionRow,
},
mixins: [draftCommentsMixin],
props: {
@@ -43,10 +45,24 @@ export default {
:data-commit-id="commitId"
class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view"
>
+ <!-- Need to insert an empty row to solve "table-layout:fixed" equal width when expansion row is the first line -->
+ <tr>
+ <td style="width: 50px;"></td>
+ <td style="width: 50px;"></td>
+ <td></td>
+ </tr>
<tbody>
<template v-for="(line, index) in diffLines">
+ <inline-diff-expansion-row
+ :key="`expand-${index}`"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
+ :line="line"
+ :is-top="index === 0"
+ :is-bottom="index + 1 === diffLinesLength"
+ />
<inline-diff-table-row
- :key="line.line_code"
+ :key="`${line.line_code || index}`"
:file-hash="diffFile.file_hash"
:context-lines-path="diffFile.context_lines_path"
:line="line"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue
new file mode 100644
index 00000000000..c1b30eab199
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/parallel_diff_expansion_row.vue
@@ -0,0 +1,56 @@
+<script>
+import { MATCH_LINE_TYPE } from '../constants';
+import DiffExpansionCell from './diff_expansion_cell.vue';
+
+export default {
+ components: {
+ DiffExpansionCell,
+ },
+ props: {
+ fileHash: {
+ type: String,
+ required: true,
+ },
+ contextLinesPath: {
+ type: String,
+ required: true,
+ },
+ line: {
+ type: Object,
+ required: true,
+ },
+ isTop: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isBottom: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ isMatchLineLeft() {
+ return this.line.left && this.line.left.type === MATCH_LINE_TYPE;
+ },
+ isMatchLineRight() {
+ return this.line.right && this.line.right.type === MATCH_LINE_TYPE;
+ },
+ },
+};
+</script>
+<template>
+ <tr class="line_expansion match">
+ <template v-if="isMatchLineLeft || isMatchLineRight">
+ <diff-expansion-cell
+ :file-hash="fileHash"
+ :context-lines-path="contextLinesPath"
+ :line="line.left"
+ :is-top="isTop"
+ :is-bottom="isBottom"
+ :colspan="4"
+ />
+ </template>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index c60246bf8ef..4c95d618b0f 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -3,6 +3,7 @@ import { mapActions, mapState } from 'vuex';
import $ from 'jquery';
import DiffTableCell from './diff_table_cell.vue';
import {
+ MATCH_LINE_TYPE,
NEW_LINE_TYPE,
OLD_LINE_TYPE,
CONTEXT_LINE_TYPE,
@@ -75,6 +76,12 @@ export default {
},
];
},
+ isMatchLineLeft() {
+ return this.line.left && this.line.left.type === MATCH_LINE_TYPE;
+ },
+ isMatchLineRight() {
+ return this.line.right && this.line.right.type === MATCH_LINE_TYPE;
+ },
},
created() {
this.newLineType = NEW_LINE_TYPE;
@@ -122,7 +129,7 @@ export default {
@mouseover="handleMouseMove"
@mouseout="handleMouseMove"
>
- <template v-if="line.left">
+ <template v-if="line.left && !isMatchLineLeft">
<diff-table-cell
:file-hash="fileHash"
:context-lines-path="contextLinesPath"
@@ -148,7 +155,7 @@ export default {
<td class="diff-line-num old_line empty-cell"></td>
<td class="line_content parallel left-side empty-cell"></td>
</template>
- <template v-if="line.right">
+ <template v-if="line.right && !isMatchLineRight">
<diff-table-cell
:file-hash="fileHash"
:context-lines-path="contextLinesPath"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index c477e68c33c..d400eb2c586 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -3,9 +3,11 @@ import { mapGetters } from 'vuex';
import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
+import parallelDiffExpansionRow from './parallel_diff_expansion_row.vue';
export default {
components: {
+ parallelDiffExpansionRow,
parallelDiffTableRow,
parallelDiffCommentRow,
ParallelDraftCommentRow: () =>
@@ -43,8 +45,23 @@ export default {
:data-commit-id="commitId"
class="code diff-wrap-lines js-syntax-highlight text-file"
>
+ <!-- Need to insert an empty row to solve "table-layout:fixed" equal width when expansion row is the first line -->
+ <tr>
+ <td style="width: 50px;"></td>
+ <td></td>
+ <td style="width: 50px;"></td>
+ <td></td>
+ </tr>
<tbody>
<template v-for="(line, index) in diffLines">
+ <parallel-diff-expansion-row
+ :key="`expand-${index}`"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
+ :line="line"
+ :is-top="index === 0"
+ :is-bottom="index + 1 === diffLinesLength"
+ />
<parallel-diff-table-row
:key="line.line_code"
:file-hash="diffFile.file_hash"
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 32e0d8f42ee..6695d9fe96c 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -109,7 +109,7 @@ export const toggleLineDiscussions = ({ commit }, options) => {
export const renderFileForDiscussionId = ({ commit, rootState, state }, discussionId) => {
const discussion = rootState.notes.discussions.find(d => d.id === discussionId);
- if (discussion) {
+ if (discussion && discussion.diff_file) {
const file = state.diffFiles.find(f => f.file_hash === discussion.diff_file.file_hash);
if (file) {
@@ -183,7 +183,7 @@ export const cancelCommentForm = ({ commit }, { lineCode, fileHash }) => {
};
export const loadMoreLines = ({ commit }, options) => {
- const { endpoint, params, lineNumbers, fileHash } = options;
+ const { endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers } = options;
params.from_merge_request = true;
@@ -195,6 +195,8 @@ export const loadMoreLines = ({ commit }, options) => {
contextLines,
params,
fileHash,
+ isExpandDown,
+ nextLineNumbers,
});
});
};
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index a66f205bbbd..a6915a46c00 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -71,18 +71,30 @@ export default {
},
[types.ADD_CONTEXT_LINES](state, options) {
- const { lineNumbers, contextLines, fileHash } = options;
+ const { lineNumbers, contextLines, fileHash, isExpandDown, nextLineNumbers } = options;
const { bottom } = options.params;
const diffFile = findDiffFile(state.diffFiles, fileHash);
removeMatchLine(diffFile, lineNumbers, bottom);
- const lines = addLineReferences(contextLines, lineNumbers, bottom).map(line => ({
- ...line,
- line_code: line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`,
- discussions: line.discussions || [],
- hasForm: false,
- }));
+ const lines = addLineReferences(
+ contextLines,
+ lineNumbers,
+ bottom,
+ isExpandDown,
+ nextLineNumbers,
+ ).map(line => {
+ const lineCode =
+ line.type === 'match'
+ ? `${fileHash}_${line.meta_data.old_pos}_${line.meta_data.new_pos}_match`
+ : line.line_code || `${fileHash}_${line.old_line}_${line.new_line}`;
+ return {
+ ...line,
+ line_code: lineCode,
+ discussions: line.discussions || [],
+ hasForm: false,
+ };
+ });
addContextLines({
inlineLines: diffFile.highlighted_diff_lines,
@@ -90,6 +102,7 @@ export default {
contextLines: lines,
bottom,
lineNumbers,
+ isExpandDown,
});
},
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 1c3ed84001c..d46bdea9b50 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -121,7 +121,7 @@ export function removeMatchLine(diffFile, lineNumbers, bottom) {
diffFile.parallel_diff_lines.splice(indexForParallel + factor, 1);
}
-export function addLineReferences(lines, lineNumbers, bottom) {
+export function addLineReferences(lines, lineNumbers, bottom, isExpandDown, nextLineNumbers) {
const { oldLineNumber, newLineNumber } = lineNumbers;
const lineCount = lines.length;
let matchLineIndex = -1;
@@ -135,15 +135,20 @@ export function addLineReferences(lines, lineNumbers, bottom) {
new_line: bottom ? newLineNumber + index + 1 : newLineNumber + index - lineCount,
});
}
-
return l;
});
if (matchLineIndex > -1) {
const line = linesWithNumbers[matchLineIndex];
- const targetLine = bottom
- ? linesWithNumbers[matchLineIndex - 1]
- : linesWithNumbers[matchLineIndex + 1];
+ let targetLine;
+
+ if (isExpandDown) {
+ targetLine = nextLineNumbers;
+ } else if (bottom) {
+ targetLine = linesWithNumbers[matchLineIndex - 1];
+ } else {
+ targetLine = linesWithNumbers[matchLineIndex + 1];
+ }
Object.assign(line, {
meta_data: {
@@ -152,26 +157,27 @@ export function addLineReferences(lines, lineNumbers, bottom) {
},
});
}
-
return linesWithNumbers;
}
export function addContextLines(options) {
- const { inlineLines, parallelLines, contextLines, lineNumbers } = options;
+ const { inlineLines, parallelLines, contextLines, lineNumbers, isExpandDown } = options;
const normalizedParallelLines = contextLines.map(line => ({
left: line,
right: line,
line_code: line.line_code,
}));
+ const factor = isExpandDown ? 1 : 0;
- if (options.bottom) {
+ if (!isExpandDown && options.bottom) {
inlineLines.push(...contextLines);
parallelLines.push(...normalizedParallelLines);
} else {
const inlineIndex = findIndexInInlineLines(inlineLines, lineNumbers);
const parallelIndex = findIndexInParallelLines(parallelLines, lineNumbers);
- inlineLines.splice(inlineIndex, 0, ...contextLines);
- parallelLines.splice(parallelIndex, 0, ...normalizedParallelLines);
+
+ inlineLines.splice(inlineIndex + factor, 0, ...contextLines);
+ parallelLines.splice(parallelIndex + factor, 0, ...normalizedParallelLines);
}
}
diff --git a/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql
new file mode 100644
index 00000000000..7403fd6d3c2
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/fragments/pageInfo.fragment.graphql
@@ -0,0 +1,4 @@
+fragment PageInfo on PageInfo {
+ hasNextPage
+ endCursor
+}
diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue
index 3cfdc1a367a..db8365a08e0 100644
--- a/app/assets/javascripts/ide/components/branches/search_list.vue
+++ b/app/assets/javascripts/ide/components/branches/search_list.vue
@@ -58,26 +58,24 @@ export default {
<template>
<div>
- <div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
- <div class="position-relative">
- <input
- ref="searchInput"
- v-model="search"
- :placeholder="__('Search branches')"
- type="search"
- class="form-control dropdown-input-field"
- @input="searchBranches"
- />
- <icon :size="18" name="search" class="input-icon" />
- </div>
- </div>
+ <label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block position-relative" @click.stop>
+ <input
+ ref="searchInput"
+ v-model="search"
+ :placeholder="__('Search branches')"
+ type="search"
+ class="form-control dropdown-input-field"
+ @input="searchBranches"
+ />
+ <icon :size="18" name="search" class="ml-3 input-icon" />
+ </label>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<gl-loading-icon
v-if="isLoading"
:size="2"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
/>
- <ul v-else class="mb-3 w-100">
+ <ul v-else class="mb-0 w-100">
<template v-if="hasBranches">
<li v-for="item in branches" :key="item.name">
<item :item="item" :project-id="currentProjectId" :is-active="isActiveBranch(item)" />
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index 2d55ffb3c65..5daf2d1422c 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -76,19 +76,17 @@ export default {
<template>
<div>
- <div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
- <div class="position-relative">
- <tokened-input
- v-model="search"
- :tokens="searchTokens"
- :placeholder="__('Search merge requests')"
- @focus="onSearchFocus"
- @input="searchMergeRequests"
- @removeToken="setSearchType(null)"
- />
- <icon :size="18" name="search" class="input-icon" />
- </div>
- </div>
+ <label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block" @click.stop>
+ <tokened-input
+ v-model="search"
+ :tokens="searchTokens"
+ :placeholder="__('Search merge requests')"
+ @focus="onSearchFocus"
+ @input="searchMergeRequests"
+ @removeToken="setSearchType(null)"
+ />
+ <icon :size="18" name="search" class="ml-3 input-icon" />
+ </label>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<gl-loading-icon
v-if="isLoading"
@@ -96,7 +94,7 @@ export default {
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
/>
<template v-else>
- <ul class="mb-3 w-100">
+ <ul class="mb-0 w-100">
<template v-if="showSearchTypes">
<li v-for="searchType in $options.searchTypes" :key="searchType.type">
<button
@@ -107,7 +105,7 @@ export default {
<span class="d-flex append-right-default ide-search-list-current-icon">
<icon :size="18" name="search" />
</span>
- <span> {{ searchType.label }} </span>
+ <span>{{ searchType.label }}</span>
</button>
</li>
</template>
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index 528ccb77efc..d48bf1fe7a9 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -70,6 +70,9 @@ export default {
hasIssuableTemplates() {
return this.issuableTemplates.length;
},
+ showLockedWarning() {
+ return this.formState.lockedWarningVisible && !this.formState.updateLoading;
+ },
},
created() {
eventHub.$on('delete.issuable', this.resetAutosave);
@@ -117,7 +120,7 @@ export default {
<template>
<form>
- <locked-warning v-if="formState.lockedWarningVisible" />
+ <locked-warning v-if="showLockedWarning" />
<div class="row">
<div v-if="hasIssuableTemplates" class="col-sm-4 col-lg-3">
<description-template
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index 842a209a545..8caac68e0d4 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -3,6 +3,7 @@ import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import store from 'ee_else_ce/mr_notes/stores';
import notesApp from '../notes/components/notes_app.vue';
+import discussionKeyboardNavigator from '../notes/components/discussion_keyboard_navigator.vue';
export default () => {
// eslint-disable-next-line no-new
@@ -56,15 +57,19 @@ export default () => {
},
},
render(createElement) {
- return createElement('notes-app', {
- props: {
- noteableData: this.noteableData,
- notesData: this.notesData,
- userData: this.currentUserData,
- shouldShow: this.activeTab === 'show',
- helpPagePath: this.helpPagePath,
- },
- });
+ const isDiffView = this.activeTab === 'diffs';
+
+ return createElement(discussionKeyboardNavigator, { props: { isDiffView } }, [
+ createElement('notes-app', {
+ props: {
+ noteableData: this.noteableData,
+ notesData: this.notesData,
+ userData: this.currentUserData,
+ shouldShow: this.activeTab === 'show',
+ helpPagePath: this.helpPagePath,
+ },
+ }),
+ ]);
},
});
};
diff --git a/app/assets/javascripts/mr_notes/stores/index.js b/app/assets/javascripts/mr_notes/stores/index.js
index c4225c8ec08..8fbd0291a7d 100644
--- a/app/assets/javascripts/mr_notes/stores/index.js
+++ b/app/assets/javascripts/mr_notes/stores/index.js
@@ -9,7 +9,7 @@ Vue.use(Vuex);
export const createStore = () =>
new Vuex.Store({
modules: {
- page: mrPageModule,
+ page: mrPageModule(),
notes: notesModule(),
diffs: diffsModule(),
},
diff --git a/app/assets/javascripts/mr_notes/stores/modules/index.js b/app/assets/javascripts/mr_notes/stores/modules/index.js
index 660081f76c8..c28e666943b 100644
--- a/app/assets/javascripts/mr_notes/stores/modules/index.js
+++ b/app/assets/javascripts/mr_notes/stores/modules/index.js
@@ -2,11 +2,11 @@ import actions from '../actions';
import getters from '../getters';
import mutations from '../mutations';
-export default {
+export default () => ({
state: {
activeTab: null,
},
actions,
getters,
mutations,
-};
+});
diff --git a/app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue b/app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue
new file mode 100644
index 00000000000..5fc2b6ba04c
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_keyboard_navigator.vue
@@ -0,0 +1,47 @@
+<script>
+/* global Mousetrap */
+import 'mousetrap';
+import { mapGetters, mapActions } from 'vuex';
+import discussionNavigation from '~/notes/mixins/discussion_navigation';
+
+export default {
+ mixins: [discussionNavigation],
+ props: {
+ isDiffView: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ currentDiscussionId: null,
+ };
+ },
+ computed: {
+ ...mapGetters(['nextUnresolvedDiscussionId', 'previousUnresolvedDiscussionId']),
+ },
+ mounted() {
+ Mousetrap.bind('n', () => this.jumpToNextDiscussion());
+ Mousetrap.bind('p', () => this.jumpToPreviousDiscussion());
+ },
+ methods: {
+ ...mapActions(['expandDiscussion']),
+ jumpToNextDiscussion() {
+ const nextId = this.nextUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
+
+ this.jumpToDiscussion(nextId);
+ this.currentDiscussionId = nextId;
+ },
+ jumpToPreviousDiscussion() {
+ const prevId = this.previousUnresolvedDiscussionId(this.currentDiscussionId, this.isDiffView);
+
+ this.jumpToDiscussion(prevId);
+ this.currentDiscussionId = prevId;
+ },
+ },
+ render() {
+ return this.$slots.default;
+ },
+};
+</script>
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 8aa8f5037b3..52410f18d4a 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -183,6 +183,15 @@ export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, dif
return slicedIds.length ? idsOrdered.slice(currentIndex + 1, currentIndex + 2)[0] : idsOrdered[0];
};
+export const previousUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) => {
+ const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder);
+ const currentIndex = idsOrdered.indexOf(discussionId);
+ const slicedIds = idsOrdered.slice(currentIndex - 1, currentIndex);
+
+ // Get the last ID if there is none after the currentIndex
+ return slicedIds.length ? slicedIds[0] : idsOrdered[idsOrdered.length - 1];
+};
+
// @param {Boolean} diffOrder - is ordered by diff?
export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
if (diffOrder) {
diff --git a/app/assets/javascripts/pages/search/show/refresh_counts.js b/app/assets/javascripts/pages/search/show/refresh_counts.js
new file mode 100644
index 00000000000..fa75ee6075d
--- /dev/null
+++ b/app/assets/javascripts/pages/search/show/refresh_counts.js
@@ -0,0 +1,24 @@
+import axios from '~/lib/utils/axios_utils';
+
+function showCount(el, count) {
+ el.textContent = count;
+ el.classList.remove('hidden');
+}
+
+function refreshCount(el) {
+ const { url } = el.dataset;
+
+ return axios
+ .get(url)
+ .then(({ data }) => showCount(el, data.count))
+ .catch(e => {
+ // eslint-disable-next-line no-console
+ console.error(`Failed to fetch search count from '${url}'.`, e);
+ });
+}
+
+export default function refreshCounts() {
+ const elements = Array.from(document.querySelectorAll('.js-search-count'));
+
+ return Promise.all(elements.map(refreshCount));
+}
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index 81b6225cb18..86ec78e1df8 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -3,6 +3,7 @@ import Flash from '~/flash';
import Api from '~/api';
import { __ } from '~/locale';
import Project from '~/pages/projects/project';
+import refreshCounts from './refresh_counts';
export default class Search {
constructor() {
@@ -14,6 +15,7 @@ export default class Search {
this.groupId = $groupDropdown.data('groupId');
this.eventListeners();
+ refreshCounts();
$groupDropdown.glDropdown({
selectable: true,
diff --git a/app/assets/javascripts/repository/queries/getFiles.query.graphql b/app/assets/javascripts/repository/queries/getFiles.query.graphql
index b3cc0878cad..c4814f8e63a 100644
--- a/app/assets/javascripts/repository/queries/getFiles.query.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.query.graphql
@@ -1,3 +1,5 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
fragment TreeEntry on Entry {
id
name
@@ -5,11 +7,6 @@ fragment TreeEntry on Entry {
type
}
-fragment PageInfo on PageInfo {
- hasNextPage
- endCursor
-}
-
query getFiles(
$projectPath: ID!
$path: String
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index c108f45622f..92190f8979e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -603,6 +603,7 @@ $blame-blue: #254e77;
* Builds
*/
$builds-trace-bg: #111;
+$job-log-highlight-height: 18px;
/*
* Commit Page
diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss
index ee0ec94c636..b3974df8639 100644
--- a/app/assets/stylesheets/highlight/white_base.scss
+++ b/app/assets/stylesheets/highlight/white_base.scss
@@ -101,6 +101,26 @@ pre.code,
color: $white-code-color;
}
+// Expansion line
+.line_expansion {
+ background-color: $gray-light;
+
+ td {
+ border-top: 1px solid $border-color;
+ border-bottom: 1px solid $border-color;
+ text-align: center;
+ }
+
+ a {
+ color: $blue-600;
+ }
+
+ .unfold-icon {
+ display: inline-block;
+ padding: 8px 0;
+ }
+}
+
// Diff line
.line_holder {
&.match .line_content,
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index cbcd8a474f1..ba126d59eef 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -1216,11 +1216,10 @@ $ide-commit-header-height: 48px;
}
.ide-search-list-empty {
- height: 230px;
+ height: 69px;
}
.ide-merge-requests-dropdown-content {
- min-height: 230px;
max-height: 470px;
}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 6fc742871e7..73166940146 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -124,6 +124,26 @@
float: left;
padding-left: $gl-padding-8;
}
+
+ .section-start {
+ display: inline;
+ }
+
+ .section-start,
+ .section-header {
+ &:hover {
+ cursor: pointer;
+
+ &::after {
+ content: '';
+ background-color: rgba($white-light, 0.2);
+ left: 0;
+ right: 0;
+ position: absolute;
+ height: $job-log-highlight-height;
+ }
+ }
+ }
}
.build-header {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 95ea49ad465..ffb27e54f34 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -408,6 +408,14 @@ table.code {
table-layout: fixed;
border-radius: 0 0 $border-radius-default $border-radius-default;
+ tr:first-of-type.line_expansion > td {
+ border-top: 0;
+ }
+
+ tr:nth-last-of-type(2).line_expansion > td {
+ border-bottom: 0;
+ }
+
tr.line_holder td {
line-height: $code-line-height;
font-size: $code-font-size;
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index d54d264bc5d..45408c9ab3c 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -67,7 +67,7 @@
position: absolute;
right: 4px;
top: 0;
- height: 35px;
+ height: $input-height;
padding-left: 10px;
padding-right: 10px;
color: $gray-darkest;
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 586365eb1ce..7b64c67ae34 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -4,7 +4,7 @@
*/
.todos-list > .todo {
- // workaround because we cannot use border-colapse
+ // workaround because we cannot use border-collapse
border-top: 1px solid transparent;
display: flex;
flex-direction: row;
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index f1d6fb00cfc..557888711ec 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -5,6 +5,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
include MembersPresentation
include SortingHelper
+ MEMBER_PER_PAGE_LIMIT = 50
+
def self.admin_not_required_endpoints
%i[index leave request_access]
end
@@ -24,7 +26,14 @@ class Groups::GroupMembersController < Groups::ApplicationController
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = GroupMembersFinder.new(@group).execute
- @members = @members.non_invite unless can_manage_members
+
+ if can_manage_members
+ @invited_members = @members.invite
+ @invited_members = @invited_members.search_invite_email(params[:search_invited]) if params[:search_invited].present?
+ @invited_members = present_members(@invited_members.page(params[:invited_members_page]).per(MEMBER_PER_PAGE_LIMIT))
+ end
+
+ @members = @members.non_invite
@members = @members.search(params[:search]) if params[:search].present?
@members = @members.sort_by_attribute(@sort)
@@ -32,7 +41,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
@members = @members.filter_by_2fa(params[:two_factor])
end
- @members = @members.page(params[:page]).per(50)
+ @members = @members.page(params[:page]).per(MEMBER_PER_PAGE_LIMIT)
@members = present_members(@members)
@requesters = present_members(
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index f71ea8642cd..dc72a4e4fd9 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::BitbucketServerController < Import::BaseController
+ include ActionView::Helpers::SanitizeHelper
+
before_action :verify_bitbucket_server_import_enabled
before_action :bitbucket_auth, except: [:new, :configure]
before_action :validate_import_params, only: [:create]
@@ -57,7 +59,7 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
- @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page)
+ @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
@repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
# Use the import URL to filter beyond what BaseService#find_already_added_projects
@@ -147,4 +149,8 @@ class Import::BitbucketServerController < Import::BaseController
def limit_per_page
BitbucketServer::Paginator::PAGE_LENGTH
end
+
+ def sanitized_filter_param
+ sanitize(params[:filter])
+ end
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 13741548687..e30935be4b6 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -36,6 +36,15 @@ class SearchController < ApplicationController
check_single_commit_result
end
+ def count
+ params.require([:search, :scope])
+
+ scope = search_service.scope
+ count = search_service.search_results.formatted_count(scope)
+
+ render json: { count: count }
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def autocomplete
term = params[:term]
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index f5c4686a3bf..91c83380b62 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -145,17 +145,27 @@ module SearchHelper
Sanitize.clean(str)
end
- def search_filter_path(options = {})
- exist_opts = {
- search: params[:search],
- project_id: params[:project_id],
- group_id: params[:group_id],
- scope: params[:scope],
- repository_ref: params[:repository_ref]
- }
+ def search_filter_link(scope, label, data: {}, search: {})
+ search_params = params
+ .merge(search)
+ .merge({ scope: scope })
+ .permit(:search, :scope, :project_id, :group_id, :repository_ref, :snippets)
+
+ if @scope == scope
+ li_class = 'active'
+ count = @search_results.formatted_count(scope)
+ else
+ badge_class = 'js-search-count hidden'
+ badge_data = { url: search_count_path(search_params) }
+ end
- options = exist_opts.merge(options)
- search_path(options)
+ content_tag :li, class: li_class, data: data do
+ link_to search_path(search_params) do
+ concat label
+ concat ' '
+ concat content_tag(:span, count, class: ['badge badge-pill', badge_class], data: badge_data)
+ end
+ end
end
def search_filter_input_options(type)
@@ -212,10 +222,6 @@ module SearchHelper
sanitize(html, tags: %w(a p ol ul li pre code))
end
- def limited_count(count, limit = 1000)
- count > limit ? "#{limit}+" : count
- end
-
def search_tabs?(tab)
return false if Feature.disabled?(:users_search, default_enabled: true)
diff --git a/app/models/label.rb b/app/models/label.rb
index 25de26b8384..d9455b36242 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -138,6 +138,8 @@ class Label < ApplicationRecord
end
def self.on_project_board?(project_id, label_id)
+ return false if label_id.blank?
+
on_project_boards(project_id).where(id: label_id).exists?
end
diff --git a/app/models/member.rb b/app/models/member.rb
index c7583434148..dbae1076670 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -107,6 +107,10 @@ class Member < ApplicationRecord
joins(:user).merge(User.search(query))
end
+ def search_invite_email(query)
+ invite.where(['invite_email ILIKE ?', "%#{query}%"])
+ end
+
def filter_by_2fa(value)
case value
when 'enabled'
diff --git a/app/models/user.rb b/app/models/user.rb
index ac83c8e3256..374e00987c5 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -438,18 +438,20 @@ class User < ApplicationRecord
order = <<~SQL
CASE
- WHEN users.name = %{query} THEN 0
- WHEN users.username = %{query} THEN 1
- WHEN users.email = %{query} THEN 2
+ WHEN users.name = :query THEN 0
+ WHEN users.username = :query THEN 1
+ WHEN users.email = :query THEN 2
ELSE 3
END
SQL
+ sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query]))
+
where(
fuzzy_arel_match(:name, query, lower_exact_match: true)
.or(fuzzy_arel_match(:username, query, lower_exact_match: true))
.or(arel_table[:email].eq(query))
- ).reorder(order % { query: ApplicationRecord.connection.quote(query) }, :name)
+ ).reorder(sanitized_order_sql, :name)
end
# Limits the result set to users _not_ in the given query/list of IDs.
diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb
index 21a1e1309e0..4c6dd6895cf 100644
--- a/app/presenters/blobs/unfold_presenter.rb
+++ b/app/presenters/blobs/unfold_presenter.rb
@@ -9,7 +9,7 @@ module Blobs
attribute :full, Boolean, default: false
attribute :since, GtOneCoercion
- attribute :to, GtOneCoercion
+ attribute :to, Integer
attribute :bottom, Boolean
attribute :unfold, Boolean, default: true
attribute :offset, Integer
@@ -24,9 +24,7 @@ module Blobs
@all_lines = blob.data.lines
super(params)
- if full?
- self.attributes = { since: 1, to: @all_lines.size, bottom: false, unfold: false, offset: 0, indent: 0 }
- end
+ self.attributes = prepare_attributes
end
# Returns an array of Gitlab::Diff::Line with match line added
@@ -43,7 +41,9 @@ module Blobs
end
def lines
- @lines ||= limit(highlight.lines).map(&:html_safe)
+ strong_memoize(:lines) do
+ limit(highlight.lines).map(&:html_safe)
+ end
end
def match_line_text
@@ -56,10 +56,34 @@ module Blobs
private
+ def prepare_attributes
+ return attributes unless full? || to == -1
+
+ full_opts = {
+ since: 1,
+ to: all_lines_size,
+ bottom: false,
+ unfold: false,
+ offset: 0,
+ indent: 0
+ }
+
+ return full_opts if full?
+
+ full_opts.merge(attributes.slice(:since))
+ end
+
+ def all_lines_size
+ strong_memoize(:all_lines_size) do
+ @all_lines.size
+ end
+ end
+
def add_match_line(diff_lines)
return unless unfold?
+ return if bottom? && to >= all_lines_size
- if bottom? && to < @all_lines.size
+ if bottom? && to < all_lines_size
old_pos = to - offset
new_pos = to
elsif since != 1
@@ -75,7 +99,9 @@ module Blobs
end
def limited_blob_lines
- @limited_blob_lines ||= limit(@all_lines)
+ strong_memoize(:limited_blob_lines) do
+ limit(@all_lines)
+ end
end
def limit(lines)
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index a09272f0d83..248e81080cc 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -53,7 +53,7 @@ module Notes
# We must add the error after we call #save because errors are reset
# when #save is called
if only_commands
- note.errors.add(:commands_only, message.presence || _('Commands did not apply'))
+ note.errors.add(:commands_only, message.presence || _('Failed to apply commands.'))
end
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 2bc04470342..caab946174d 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -122,7 +122,7 @@ module Projects
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
log_error("Could not create wiki for #{project.full_name}")
- Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
+ Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki').increment
end
def update_pages_config
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 2b8c9f65d43..882fcc79421 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,39 +1,67 @@
-- page_title "Members"
+- page_title _("Members")
- can_manage_members = can?(current_user, :admin_group_member, @group)
+- show_invited_members = can_manage_members && @invited_members.exists?
+- pending_active = params[:search_invited].present?
.project-members-page.prepend-top-default
%h4
- Members
+ = _("Members")
%hr
- if can_manage_members
.project-members-new.append-bottom-default
%p.clearfix
- Add new member to
- %strong= @group.name
+ = _("Add new member to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
= render "new_group_member"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
= render_if_exists 'groups/group_members/ldap_sync'
- .clearfix
- %h5.member.existing-title
- Existing members
- .card
- .card-header.flex-project-members-panel
- %span.flex-project-title
- Members with access to
- %strong= @group.name
- %span.badge.badge-pill= @members.total_count
- = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form flex-users-form' do
- .form-group
- .position-relative.append-right-8
- = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
- %button.user-search-btn{ type: "submit", "aria-label" => "Submit search" }
- = icon("search")
- - if can_manage_members
- = render 'shared/members/filter_2fa_dropdown'
- = render 'shared/members/sort_dropdown'
- %ul.content-list.members-list
- = render partial: 'shared/members/member', collection: @members, as: :member
- = paginate @members, theme: 'gitlab'
+ %ul.nav-links.mobile-separator.nav.nav-tabs.clearfix
+ %li.nav-item
+ = link_to "#existing_members", class: ["nav-link", ("active" unless pending_active)] , 'data-toggle' => 'tab' do
+ %span
+ = _("Existing")
+ %span.badge.badge-pill= @members.total_count
+ - if show_invited_members
+ %li.nav-item
+ = link_to "#invited_members", class: ["nav-link", ("active" if pending_active)], 'data-toggle' => 'tab' do
+ %span
+ = _("Pending")
+ %span.badge.badge-pill= @invited_members.total_count
+
+ .tab-content
+ #existing_members.tab-pane{ :class => ("active" unless pending_active) }
+ .card.card-without-border
+ .d-flex.flex-column.flex-md-row.row-content-block.second-block
+ %span.flex-grow-1.align-self-md-center.col-form-label
+ = _("Members with access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
+ .form-group.flex-grow
+ .position-relative.mr-md-2
+ = search_field_tag :search, params[:search], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
+ %button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
+ = icon("search")
+ - if can_manage_members
+ = label_tag '2fa', '2FA', class: 'col-form-label label-bold pr-md-2'
+ = render 'shared/members/filter_2fa_dropdown'
+ = render 'shared/members/sort_dropdown'
+ %ul.content-list.members-list
+ = render partial: 'shared/members/member', collection: @members, as: :member
+ = paginate @members, theme: 'gitlab'
+
+ - if show_invited_members
+ #invited_members.tab-pane{ :class => ("active" if pending_active) }
+ .card.card-without-border
+ .d-flex.flex-column.flex-md-row.row-content-block.second-block
+ %span.flex-grow-1
+ = _("Members with pending access to %{strong_start}%{group_name}%{strong_end}").html_safe % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe }
+ = form_tag group_group_members_path(@group), method: :get, class: 'form-inline user-search-form' do
+ .form-group
+ .position-relative.mr-md-2
+ = search_field_tag :search_invited, params[:search_invited], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
+ %button.user-search-btn.border-left{ type: "submit", "aria-label" => _("Submit search") }
+ = icon("search")
+ %ul.content-list.members-list
+ = render partial: 'shared/members/member', collection: @invited_members, as: :member
+ = paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab'
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 46d7c367aa7..a996c86a256 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -3,9 +3,9 @@
.modal-content
.modal-header
%h4.modal-title
- Keyboard Shortcuts
+ = _('Keyboard Shortcuts')
%small
- = link_to '(Show all)', '#', class: 'js-more-help-button'
+ = link_to _('(Show all)'), '#', class: 'js-more-help-button'
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
%span{ "aria-hidden": true } &times;
.modal-body
@@ -15,281 +15,281 @@
%tbody
%tr
%th
- %th Global Shortcuts
+ %th= _('Global Shortcuts')
%tr
%td.shortcut
%kbd s
- %td Focus Search
+ %td= _('Focus Search')
%tr
%td.shortcut
%kbd f
- %td Focus Filter
+ %td= _('Focus Filter')
%tr
%td.shortcut
%kbd p
%kbd b
- %td Toggle the Performance Bar
+ %td= _('Toggle the Performance Bar')
%tr
%td.shortcut
%kbd ?
- %td Show/hide this dialog
+ %td= _('Show/hide this dialog')
%tr
%td.shortcut
- if browser.platform.mac?
%kbd &#8984; shift p
- else
%kbd ctrl shift p
- %td Toggle Markdown preview
+ %td= _('Toggle Markdown preview')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-up
- %td Edit last comment (when focused on an empty textarea)
+ %td= _('Edit last comment (when focused on an empty textarea)')
%tr
%td.shortcut
%kbd shift t
%td
- Go to todos
+ = _('Go to todos')
%tr
%td.shortcut
%kbd shift a
%td
- Go to the activity feed
+ = _('Go to the activity feed')
%tr
%td.shortcut
%kbd shift p
%td
- Go to projects
+ = _('Go to projects')
%tr
%td.shortcut
%kbd shift i
%td
- Go to issues
+ = _('Go to issues')
%tr
%td.shortcut
%kbd shift m
%td
- Go to merge requests
+ = _('Go to merge requests')
%tr
%td.shortcut
%kbd shift g
%td
- Go to groups
+ = _('Go to groups')
%tr
%td.shortcut
%kbd shift l
%td
- Go to milestones
+ = _('Go to milestones')
%tr
%td.shortcut
%kbd shift s
%td
- Go to snippets
+ = _('Go to snippets')
%tbody
%tr
%th
- %th Finding Project File
+ %th= _('Finding Project File')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-up
- %td Move selection up
+ %td= _('Move selection up')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-down
- %td Move selection down
+ %td= _('Move selection down')
%tr
%td.shortcut
%kbd enter
- %td Open Selection
+ %td= _('Open Selection')
%tr
%td.shortcut
%kbd esc
- %td Go back
+ %td= _('Go back')
.col-lg-4
%table.shortcut-mappings
%tbody
%tr
%th
- %th Project
+ %th= _('Project')
%tr
%td.shortcut
%kbd g
%kbd p
%td
- Go to the project's overview page
+ = _('Go to the project\'s overview page')
%tr
%td.shortcut
%kbd g
%kbd v
%td
- Go to the project's activity feed
+ = _('Go to the project\'s activity feed')
%tr
%td.shortcut
%kbd g
%kbd f
%td
- Go to files
+ = _('Go to files')
%tr
%td.shortcut
%kbd g
%kbd c
%td
- Go to commits
+ = _('Go to commits')
%tr
%td.shortcut
%kbd g
%kbd j
%td
- Go to jobs
+ = _('Go to jobs')
%tr
%td.shortcut
%kbd g
%kbd n
%td
- Go to network graph
+ = _('Go to network graph')
%tr
%td.shortcut
%kbd g
%kbd d
%td
- Go to repository charts
+ = _('Go to repository charts')
%tr
%td.shortcut
%kbd g
%kbd i
%td
- Go to issues
+ = _('Go to issues')
%tr
%td.shortcut
%kbd g
%kbd b
%td
- Go to issue boards
+ = _('Go to issue boards')
%tr
%td.shortcut
%kbd g
%kbd m
%td
- Go to merge requests
+ = _('Go to merge requests')
%tr
%td.shortcut
%kbd g
%kbd e
%td
- Go to environments
+ = _('Go to environments')
%tr
%td.shortcut
%kbd g
%kbd l
%td
- Go to metrics
+ = _('Go to metrics')
%tr
%td.shortcut
%kbd g
%kbd k
%td
- Go to kubernetes
+ = _('Go to kubernetes')
%tr
%td.shortcut
%kbd g
%kbd s
%td
- Go to snippets
+ = _('Go to snippets')
%tr
%td.shortcut
%kbd g
%kbd w
%td
- Go to wiki
+ = _('Go to wiki')
%tr
%td.shortcut
%kbd t
- %td Go to finding file
+ %td= _('Go to finding file')
%tr
%td.shortcut
%kbd i
- %td New issue
+ %td= _('New issue')
%tbody
%tr
%th
- %th Project Files browsing
+ %th= _('Project Files browsing')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-up
- %td Move selection up
+ %td= _('Move selection up')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-down
- %td Move selection down
+ %td= _('Move selection down')
%tr
%td.shortcut
%kbd enter
- %td Open Selection
+ %td= _('Open Selection')
%tbody
%tr
%th
- %th Project File
+ %th= _('Project File')
%tr
%td.shortcut
%kbd y
- %td Go to file permalink
+ %td= _('Go to file permalink')
%tbody
%tr
%th
- %th Web IDE
+ %th= _('Web IDE')
%tr
%td.shortcut
- if browser.platform.mac?
%kbd &#8984; p
- else
%kbd ctrl p
- %td Go to file
+ %td= _('Go to file')
%tr
%td.shortcut
- if browser.platform.mac?
%kbd &#8984; enter
- else
%kbd ctrl enter
- %td Commit (when editing commit message)
+ %td= _('Commit (when editing commit message)')
.col-lg-4
%table.shortcut-mappings
- %tbody.hidden-shortcut{ style: 'display:none' }
+ %tbody.hidden-shortcut.network{ style: 'display:none' }
%tr
%th
- %th Network Graph
+ %th= _('Network Graph')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-left
\/
%kbd h
- %td Scroll left
+ %td= _('Scroll left')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-right
\/
%kbd l
- %td Scroll right
+ %td= _('Scroll right')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-up
\/
%kbd k
- %td Scroll up
+ %td= _('Scroll up')
%tr
%td.shortcut
%kbd
%i.fa.fa-arrow-down
\/
%kbd j
- %td Scroll down
+ %td= _('Scroll down')
%tr
%td.shortcut
%kbd
@@ -298,7 +298,7 @@
\/
%kbd
shift k
- %td Scroll to top
+ %td= _('Scroll to top')
%tr
%td.shortcut
%kbd
@@ -307,72 +307,80 @@
\/
%kbd
shift j
- %td Scroll to bottom
- %tbody.hidden-shortcut{ style: 'display:none' }
+ %td= _('Scroll to bottom')
+ %tbody.hidden-shortcut.issues{ style: 'display:none' }
%tr
%th
- %th Issues
+ %th= _('Issues')
%tr
%td.shortcut
%kbd a
- %td Change assignee
+ %td= _('Change assignee')
%tr
%td.shortcut
%kbd m
- %td Change milestone
+ %td= _('Change milestone')
%tr
%td.shortcut
%kbd r
- %td Reply (quoting selected text)
+ %td= _('Reply (quoting selected text)')
%tr
%td.shortcut
%kbd e
- %td Edit issue
+ %td= _('Edit issue')
%tr
%td.shortcut
%kbd l
- %td Change Label
- %tbody.hidden-shortcut{ style: 'display:none' }
+ %td= _('Change Label')
+ %tbody.hidden-shortcut.merge_requests{ style: 'display:none' }
%tr
%th
- %th Merge Requests
+ %th= _('Merge Requests')
%tr
%td.shortcut
%kbd a
- %td Change assignee
+ %td= _('Change assignee')
%tr
%td.shortcut
%kbd m
- %td Change milestone
+ %td= _('Change milestone')
%tr
%td.shortcut
%kbd r
- %td Reply (quoting selected text)
+ %td= _('Reply (quoting selected text)')
%tr
%td.shortcut
%kbd e
- %td Edit merge request
+ %td= _('Edit merge request')
%tr
%td.shortcut
%kbd l
- %td Change Label
+ %td= _('Change Label')
%tr
%td.shortcut
%kbd ]
\/
%kbd j
- %td Move to next file
+ %td= _('Move to next file')
%tr
%td.shortcut
%kbd [
\/
%kbd k
- %td Move to previous file
- %tbody.hidden-shortcut{ style: 'display:none' }
+ %td= _('Move to previous file')
+ %tr
+ %td.shortcut
+ %kbd n
+ %td= _('Move to next unresolved discussion')
+ %tr
+ %td.shortcut
+ %kbd p
+ %td= _('Move to previous unresolved discussion')
+ %tbody.hidden-shortcut.wiki{ style: 'display:none' }
%tr
%th
- %th Wiki pages
+ %th= _('Wiki pages')
%tr
%td.shortcut
%kbd e
- %td Edit wiki page
+ %td= _('Edit wiki page')
diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml
index 40609fddbde..aac09801d91 100644
--- a/app/views/import/bitbucket_server/status.html.haml
+++ b/app/views/import/bitbucket_server/status.html.haml
@@ -17,8 +17,13 @@
= button_tag class: 'btn btn-import btn-success js-import-all' do
= _('Import all projects')
= icon('spinner spin', class: 'loading-icon')
- .btn-group
- = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
+
+.btn-group
+ = link_to('Reconfigure', configure_import_bitbucket_server_path, class: 'btn btn-primary', method: :post)
+
+.input-btn-group.float-right
+ = form_tag status_import_bitbucket_server_path, :method => 'get' do
+ = text_field_tag :filter, sanitize(params[:filter]), class: 'form-control append-bottom-10', placeholder: _('Filter your projects by name'), size: 40, autoFocus: true
.table-responsive.prepend-top-10
%table.table.import-jobs
@@ -62,7 +67,7 @@
= text_field_tag :path, current_user.namespace_path, class: "input-group-text input-large form-control", tabindex: 1, disabled: true
%span.input-group-prepend
.input-group-text /
- = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
+ = text_field_tag :path, sanitize_project_name(repo.slug), class: "input-mini form-control", tabindex: 2, required: true
%td.import-actions.job-status
= button_tag class: 'btn btn-import js-add-to-import' do
Import
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index bc900992cb0..e6a235e39da 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -47,5 +47,6 @@
= hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref
= hidden_field_tag :nav_source, 'navbar'
+ -# workaround for non-JS feature specs, for JS you need to use find('#search').send_keys(:enter)
= button_tag 'Go' if ENV['RAILS_ENV'] == 'test'
.search-autocomplete-opts.hide{ :'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 4759991449e..de0c21e7cf6 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -45,7 +45,7 @@
%ul.controls
- if issue.closed?
%li.issuable-status
- CLOSED
+ = _('CLOSED')
- if issue.assignees.any?
%li
= render 'shared/issuable/assignees', project: @project, issuable: issue
@@ -53,4 +53,5 @@
= render 'shared/issuable_meta_data', issuable: issue
.float-right.issuable-updated-at.d-none.d-sm-inline-block
- %span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
+ %span
+ = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago') }
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index dee3931ff04..36f19ee6175 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -41,11 +41,11 @@
%ul.controls.d-flex.align-items-end
- if merge_request.merged?
%li.issuable-status.d-none.d-sm-inline-block
- MERGED
+ = _('MERGED')
- elsif merge_request.closed?
%li.issuable-status.d-none.d-sm-inline-block
= icon('ban')
- CLOSED
+ = _('CLOSED')
- if can?(current_user, :read_pipeline, merge_request.head_pipeline)
%li.issuable-pipeline-status.d-none.d-sm-flex
= render 'ci/status/icon', status: merge_request.head_pipeline.detailed_status(current_user), option_css_classes: 'd-flex'
@@ -61,4 +61,5 @@
= render 'shared/issuable_meta_data', issuable: merge_request
.float-right.issuable-updated-at.d-none.d-sm-inline-block
- %span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
+ %span
+ = _('updated %{time_ago}').html_safe % { time_ago: time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago') }
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index 18613ff4c16..eae2a491ceb 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -1,10 +1,6 @@
- users = capture_haml do
- if search_tabs?(:members)
- %li{ class: active_when(@scope == 'users') }
- = link_to search_filter_path(scope: 'users') do
- Users
- %span.badge.badge-pill
- = limited_count(@search_results.limited_users_count)
+ = search_filter_link 'users', _("Users")
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
@@ -12,80 +8,28 @@
%ul.nav-links.search-filter.scrolling-tabs.nav.nav-tabs
- if @project
- if project_search_tabs?(:blobs)
- %li{ class: active_when(@scope == 'blobs'), data: { qa_selector: 'code_tab' } }
- = link_to search_filter_path(scope: 'blobs') do
- = _("Code")
- %span.badge.badge-pill
- = @search_results.blobs_count
+ = search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' }
- if project_search_tabs?(:issues)
- %li{ class: active_when(@scope == 'issues') }
- = link_to search_filter_path(scope: 'issues') do
- = _("Issues")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_issues_count)
+ = search_filter_link 'issues', _("Issues")
- if project_search_tabs?(:merge_requests)
- %li{ class: active_when(@scope == 'merge_requests') }
- = link_to search_filter_path(scope: 'merge_requests') do
- = _("Merge requests")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_merge_requests_count)
+ = search_filter_link 'merge_requests', _("Merge requests")
- if project_search_tabs?(:milestones)
- %li{ class: active_when(@scope == 'milestones') }
- = link_to search_filter_path(scope: 'milestones') do
- = _("Milestones")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_milestones_count)
+ = search_filter_link 'milestones', _("Milestones")
- if project_search_tabs?(:notes)
- %li{ class: active_when(@scope == 'notes') }
- = link_to search_filter_path(scope: 'notes') do
- = _("Comments")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_notes_count)
+ = search_filter_link 'notes', _("Comments")
- if project_search_tabs?(:wiki)
- %li{ class: active_when(@scope == 'wiki_blobs') }
- = link_to search_filter_path(scope: 'wiki_blobs') do
- = _("Wiki")
- %span.badge.badge-pill
- = @search_results.wiki_blobs_count
+ = search_filter_link 'wiki_blobs', _("Wiki")
- if project_search_tabs?(:commits)
- %li{ class: active_when(@scope == 'commits') }
- = link_to search_filter_path(scope: 'commits') do
- = _("Commits")
- %span.badge.badge-pill
- = @search_results.commits_count
+ = search_filter_link 'commits', _("Commits")
= users
- elsif @show_snippets
- %li{ class: active_when(@scope == 'snippet_blobs') }
- = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do
- = _("Snippet Contents")
- %span.badge.badge-pill
- = @search_results.snippet_blobs_count
- %li{ class: active_when(@scope == 'snippet_titles') }
- = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do
- = _("Titles and Filenames")
- %span.badge.badge-pill
- = @search_results.snippet_titles_count
+ = search_filter_link 'snippet_blobs', _("Snippet Contents"), search: { snippets: true, group_id: nil, project_id: nil }
+ = search_filter_link 'snippet_titles', _("Titles and Filenames"), search: { snippets: true, group_id: nil, project_id: nil }
- else
- %li{ class: active_when(@scope == 'projects') }
- = link_to search_filter_path(scope: 'projects') do
- = _("Projects")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_projects_count)
- %li{ class: active_when(@scope == 'issues') }
- = link_to search_filter_path(scope: 'issues') do
- = _("Issues")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_issues_count)
- %li{ class: active_when(@scope == 'merge_requests') }
- = link_to search_filter_path(scope: 'merge_requests') do
- = _("Merge requests")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_merge_requests_count)
- %li{ class: active_when(@scope == 'milestones') }
- = link_to search_filter_path(scope: 'milestones') do
- = _("Milestones")
- %span.badge.badge-pill
- = limited_count(@search_results.limited_milestones_count)
+ = search_filter_link 'projects', _("Projects")
+ = search_filter_link 'issues', _("Issues")
+ = search_filter_link 'merge_requests', _("Merge requests")
+ = search_filter_link 'milestones', _("Milestones")
= render_if_exists 'search/category_elasticsearch'
= users
diff --git a/app/views/shared/members/_filter_2fa_dropdown.html.haml b/app/views/shared/members/_filter_2fa_dropdown.html.haml
index 3e98587aeaa..44ea844028e 100644
--- a/app/views/shared/members/_filter_2fa_dropdown.html.haml
+++ b/app/views/shared/members/_filter_2fa_dropdown.html.haml
@@ -1,7 +1,7 @@
- filter = params[:two_factor] || 'everyone'
- filter_options = { 'everyone' => _('Everyone'), 'enabled' => _('Enabled'), 'disabled' => _('Disabled') }
-.dropdown.inline.member-filter-2fa-dropdown
- = dropdown_toggle('2FA: ' + filter_options[filter], { toggle: 'dropdown' })
+.dropdown.inline.member-filter-2fa-dropdown.pr-md-2
+ = dropdown_toggle(filter_options[filter], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
= _("Filter by two-factor authentication")
diff --git a/app/views/shared/members/_sort_dropdown.html.haml b/app/views/shared/members/_sort_dropdown.html.haml
index f5ebab035db..feca109dade 100644
--- a/app/views/shared/members/_sort_dropdown.html.haml
+++ b/app/views/shared/members/_sort_dropdown.html.haml
@@ -1,4 +1,5 @@
-.dropdown.inline.user-sort-dropdown
+= label_tag :sort_by, 'Sort by', class: 'col-form-label label-bold pr-2'
+.dropdown.inline.qa-user-sort-dropdown
= dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
diff --git a/changelogs/unreleased/44036-someone-edited-the-issue-at-the-same-time-is-regularly-seen-despite-that-being-a-filthy-lie.yml b/changelogs/unreleased/44036-someone-edited-the-issue-at-the-same-time-is-regularly-seen-despite-that-being-a-filthy-lie.yml
new file mode 100644
index 00000000000..674d53286e6
--- /dev/null
+++ b/changelogs/unreleased/44036-someone-edited-the-issue-at-the-same-time-is-regularly-seen-despite-that-being-a-filthy-lie.yml
@@ -0,0 +1,5 @@
+---
+title: Fix flashing conflict warning when editing issues
+merge_request: 31469
+author:
+type: fixed
diff --git a/changelogs/unreleased/58035-expand-mr-diff.yml b/changelogs/unreleased/58035-expand-mr-diff.yml
new file mode 100644
index 00000000000..7163cce29f2
--- /dev/null
+++ b/changelogs/unreleased/58035-expand-mr-diff.yml
@@ -0,0 +1,5 @@
+---
+title: Add new expansion options for merge request diffs
+merge_request: 30927
+author:
+type: added
diff --git a/changelogs/unreleased/59590-keyboard-shortcut-for-jump-to-next-unresolved-discussion.yml b/changelogs/unreleased/59590-keyboard-shortcut-for-jump-to-next-unresolved-discussion.yml
new file mode 100644
index 00000000000..02e81c7fc87
--- /dev/null
+++ b/changelogs/unreleased/59590-keyboard-shortcut-for-jump-to-next-unresolved-discussion.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Keyboard shortcut for jump to NEXT unresolved discussion
+merge_request: 30144
+author:
+type: added
diff --git a/changelogs/unreleased/61332-web-ide-mr-branch-dropdown-closes-unexpectedly.yml b/changelogs/unreleased/61332-web-ide-mr-branch-dropdown-closes-unexpectedly.yml
new file mode 100644
index 00000000000..1f5e507d48d
--- /dev/null
+++ b/changelogs/unreleased/61332-web-ide-mr-branch-dropdown-closes-unexpectedly.yml
@@ -0,0 +1,5 @@
+---
+title: Fix an issue where clicking outside the MR/branch search box in WebIDE closed the dropdown.
+merge_request: 31523
+author:
+type: fixed
diff --git a/changelogs/unreleased/63942-remove-config-action_dispatch-use_authenticated_cookie_encryption-configuration.yml b/changelogs/unreleased/63942-remove-config-action_dispatch-use_authenticated_cookie_encryption-configuration.yml
new file mode 100644
index 00000000000..741763403a5
--- /dev/null
+++ b/changelogs/unreleased/63942-remove-config-action_dispatch-use_authenticated_cookie_encryption-configuration.yml
@@ -0,0 +1,5 @@
+---
+title: Enable authenticated cookie encryption
+merge_request: 31463
+author:
+type: other
diff --git a/changelogs/unreleased/65278-fix-puma-master-counter-wipe.yml b/changelogs/unreleased/65278-fix-puma-master-counter-wipe.yml
new file mode 100644
index 00000000000..fb9d6fa251d
--- /dev/null
+++ b/changelogs/unreleased/65278-fix-puma-master-counter-wipe.yml
@@ -0,0 +1,5 @@
+---
+title: Fix active metric files being wiped after the app starts
+merge_request: 31668
+author:
+type: fixed
diff --git a/changelogs/unreleased/65530-add-externalization-and-fix-regression-in-shortcuts-helper-modal.yml b/changelogs/unreleased/65530-add-externalization-and-fix-regression-in-shortcuts-helper-modal.yml
new file mode 100644
index 00000000000..fc29a514c42
--- /dev/null
+++ b/changelogs/unreleased/65530-add-externalization-and-fix-regression-in-shortcuts-helper-modal.yml
@@ -0,0 +1,6 @@
+---
+title: Fixed display of some sections and externalized all text in the shortcuts modal
+ overlay
+merge_request: 31594
+author:
+type: fixed
diff --git a/changelogs/unreleased/65790-highlight.yml b/changelogs/unreleased/65790-highlight.yml
new file mode 100644
index 00000000000..2531a3730ed
--- /dev/null
+++ b/changelogs/unreleased/65790-highlight.yml
@@ -0,0 +1,5 @@
+---
+title: Adds highlight to the collapsible section
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/alipniagov-fix-wiki_can_not_be_created_total-counter.yml b/changelogs/unreleased/alipniagov-fix-wiki_can_not_be_created_total-counter.yml
new file mode 100644
index 00000000000..58f969ed742
--- /dev/null
+++ b/changelogs/unreleased/alipniagov-fix-wiki_can_not_be_created_total-counter.yml
@@ -0,0 +1,5 @@
+---
+title: Fix :wiki_can_not_be_created_total counter
+merge_request: 31673
+author:
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-51260-add-filtering-to-bitbucket-server-import.yml b/changelogs/unreleased/georgekoltsov-51260-add-filtering-to-bitbucket-server-import.yml
new file mode 100644
index 00000000000..c455b4cf642
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-51260-add-filtering-to-bitbucket-server-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add BitBucketServer project import filtering
+merge_request: 31420
+author:
+type: added
diff --git a/changelogs/unreleased/georgekoltsov-63408-user-mapping.yml b/changelogs/unreleased/georgekoltsov-63408-user-mapping.yml
new file mode 100644
index 00000000000..451aac9c2e3
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-63408-user-mapping.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix missing author line (`Created by: <user>`) in MRs/issues/comments of imported Bitbucket Cloud project'
+merge_request: 31579
+author:
+type: fixed
diff --git a/changelogs/unreleased/gitaly-version-v1.59.0.yml b/changelogs/unreleased/gitaly-version-v1.59.0.yml
new file mode 100644
index 00000000000..d103f6b248e
--- /dev/null
+++ b/changelogs/unreleased/gitaly-version-v1.59.0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Gitaly v1.59.0
+merge_request: 31743
+author:
+type: changed
diff --git a/changelogs/unreleased/improve-quick-action-messages.yml b/changelogs/unreleased/improve-quick-action-messages.yml
new file mode 100644
index 00000000000..86f8c251860
--- /dev/null
+++ b/changelogs/unreleased/improve-quick-action-messages.yml
@@ -0,0 +1,5 @@
+---
+title: Improve quick action error messages
+merge_request: 31451
+author:
+type: other
diff --git a/changelogs/unreleased/load-search-counts-async.yml b/changelogs/unreleased/load-search-counts-async.yml
new file mode 100644
index 00000000000..1f466450e76
--- /dev/null
+++ b/changelogs/unreleased/load-search-counts-async.yml
@@ -0,0 +1,5 @@
+---
+title: Load search result counts asynchronously
+merge_request: 31663
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-break-out-invited-group-members.yml b/changelogs/unreleased/sh-break-out-invited-group-members.yml
new file mode 100644
index 00000000000..091f1d48843
--- /dev/null
+++ b/changelogs/unreleased/sh-break-out-invited-group-members.yml
@@ -0,0 +1,5 @@
+---
+title: Make it easier to find invited group members
+merge_request: 28436
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-import-export-suggestions.yml b/changelogs/unreleased/sh-fix-import-export-suggestions.yml
new file mode 100644
index 00000000000..4b15fc3858f
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-import-export-suggestions.yml
@@ -0,0 +1,5 @@
+---
+title: Properly save suggestions in project exports
+merge_request: 31690
+author:
+type: fixed
diff --git a/changelogs/unreleased/update-graphicsmagick-to-1-3-33.yml b/changelogs/unreleased/update-graphicsmagick-to-1-3-33.yml
new file mode 100644
index 00000000000..898fdef830a
--- /dev/null
+++ b/changelogs/unreleased/update-graphicsmagick-to-1-3-33.yml
@@ -0,0 +1,5 @@
+---
+title: Update GraphicsMagick from 1.3.29 to 1.3.33 for CI tests
+merge_request: 31692
+author: Takuya Noguchi
+type: other
diff --git a/config.ru b/config.ru
index 6f6fb85d8fa..f6a7dca0542 100644
--- a/config.ru
+++ b/config.ru
@@ -17,7 +17,25 @@ end
require ::File.expand_path('../config/environment', __FILE__)
+# The following is necessary to ensure stale Prometheus metrics don't accumulate over time.
+# It needs to be done as early as here to ensure metrics files aren't deleted.
+# After we hit our app in `warmup`, first metrics and corresponding files already being created,
+# for example in `lib/gitlab/metrics/requests_rack_middleware.rb`.
+def cleanup_prometheus_multiproc_dir
+ if dir = ::Prometheus::Client.configuration.multiprocess_files_dir
+ old_metrics = Dir[File.join(dir, '*.db')]
+
+ FileUtils.rm_rf(old_metrics)
+ end
+end
+
+def master_process?
+ Prometheus::PidProvider.worker_id.in? %w(unicorn_master puma_master)
+end
+
warmup do |app|
+ cleanup_prometheus_multiproc_dir if master_process?
+
client = Rack::MockRequest.new(app)
client.get('/')
end
diff --git a/config/application.rb b/config/application.rb
index 47c5ab71285..733f8652286 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -293,10 +293,5 @@ module Gitlab
Gitlab::Routing.add_helpers(project_url_helpers)
Gitlab::Routing.add_helpers(MilestonesRoutingHelper)
end
-
- # This makes generated cookies to be compatible with Rails 5.1 and older
- # We can remove this when we're confident that there are no issues with the Rails 5.2 upgrade
- # and we won't need to rollback to older versions
- config.action_dispatch.use_authenticated_cookie_encryption = false
end
end
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index 3f2691dde95..70e5dcd042e 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -51,22 +51,3 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
end
end
end
-
-def cleanup_prometheus_multiproc_dir
- # The following is necessary to ensure stale Prometheus metrics don't
- # accumulate over time. It needs to be done in this hook as opposed to
- # inside an init script to ensure metrics files aren't deleted after new
- # unicorn workers start after a SIGUSR2 is received.
- if dir = ::Prometheus::Client.configuration.multiprocess_files_dir
- old_metrics = Dir[File.join(dir, '*.db')]
- FileUtils.rm_rf(old_metrics)
- end
-end
-
-Gitlab::Cluster::LifecycleEvents.on_master_start do
- cleanup_prometheus_multiproc_dir
-end
-
-Gitlab::Cluster::LifecycleEvents.on_master_restart do
- cleanup_prometheus_multiproc_dir
-end
diff --git a/config/routes.rb b/config/routes.rb
index 459f2b22bf0..fdef31429f3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -58,6 +58,7 @@ Rails.application.routes.draw do
# Search
get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
+ get 'search/count' => 'search#count', as: :search_count
# JSON Web Token
get 'jwt/auth' => 'jwt#auth'
diff --git a/db/migrate/20190612111201_add_geo_container_repository_counters.rb b/db/migrate/20190612111201_add_geo_container_repository_counters.rb
new file mode 100644
index 00000000000..b73bb885ab7
--- /dev/null
+++ b/db/migrate/20190612111201_add_geo_container_repository_counters.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddGeoContainerRepositoryCounters < ActiveRecord::Migration[5.1]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ change_table :geo_node_statuses do |t|
+ t.column :container_repositories_count, :integer
+ t.column :container_repositories_synced_count, :integer
+ t.column :container_repositories_failed_count, :integer
+ t.column :container_repositories_registry_count, :integer
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 6674f412140..591758af0e4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1429,6 +1429,10 @@ ActiveRecord::Schema.define(version: 2019_08_06_071559) do
t.integer "repositories_retrying_verification_count"
t.integer "wikis_retrying_verification_count"
t.integer "projects_count"
+ t.integer "container_repositories_count"
+ t.integer "container_repositories_synced_count"
+ t.integer "container_repositories_failed_count"
+ t.integer "container_repositories_registry_count"
t.index ["geo_node_id"], name: "index_geo_node_statuses_on_geo_node_id", unique: true
end
diff --git a/doc/README.md b/doc/README.md
index edce03baec9..c60e4eb177d 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -9,7 +9,7 @@ description: 'Learn how to use and administer GitLab, the most scalable Git-base
</div>
<!-- the div above will not display on the docs site but will display on /help -->
-# GitLab Documentation
+# GitLab Docs
Welcome to [GitLab](https://about.gitlab.com/) Documentation.
diff --git a/doc/administration/auditor_users.md b/doc/administration/auditor_users.md
index 65d36612d85..18c415b5ff7 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](img/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/oidc.md b/doc/administration/auth/oidc.md
index 78d040cda99..3445f916ef4 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -74,43 +74,38 @@ The OpenID Connect will provide you with a client details and secret for you to
}
```
- > **Note:**
- >
- > - For more information on each configuration option refer to
- the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage) and
- the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
+ NOTE: **Note:**
+ For more information on each configuration option refer to the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage)
+ and the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
1. For the configuration above, change the values for the provider to match your OpenID Connect client setup. Use the following as a guide:
- - `<your_oidc_label>` is the label that will be displayed on the login page.
- - `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`.
- If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
- - If `discovery` is set to `true`, the OpenID Connect provider will try to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`.
- - `client_auth_method` (optional) specifies the method used for authenticating the client with the OpenID Connect provider.
- - Supported values are:
- - `basic` - HTTP Basic Authentication
- - `jwt_bearer` - JWT based authentication (private key and client secret signing)
- - `mtls` - Mutual TLS or X.509 certificate validation
- - Any other value will POST the client id and secret in the request body
- - If not specified, defaults to `basic`.
- - `<uid_field>` (optional) is the field name from the `user_info` details that will be used as `uid` value. For example, `preferred_username`.
- If this value is not provided or the field with the configured value is missing from the `user_info` details, the `uid` will use the `sub` field.
- - `client_options` are the OpenID Connect client-specific options. Specifically:
-
- - `identifier` is the client identifier as configured in the OpenID Connect service provider.
- - `secret` is the client secret as configured in the OpenID Connect service provider.
- - `redirect_uri` is the GitLab URL to redirect the user to after successful login. For example, `http://example.com/users/auth/openid_connect/callback`.
- - `end_session_endpoint` (optional) is the URL to the endpoint that end the session (logout). Can be provided if auto-discovery disabled or unsuccessful.
-
- The following `client_options` are optional unless auto-discovery is disabled or unsuccessful:
-
- - `authorization_endpoint` is the URL to the endpoint that authorizes the end user.
- - `token_endpoint` is the URL to the endpoint that provides Access Token.
- - `userinfo_endpoint` is the URL to the endpoint that provides the user information.
- - `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys.
-
-1. Save the configuration file.
-1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
- for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+ - `<your_oidc_label>` is the label that will be displayed on the login page.
+ - `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`.
+ If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
+ - If `discovery` is set to `true`, the OpenID Connect provider will try to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`.
+ - `client_auth_method` (optional) specifies the method used for authenticating the client with the OpenID Connect provider.
+ - Supported values are:
+ - `basic` - HTTP Basic Authentication
+ - `jwt_bearer` - JWT based authentication (private key and client secret signing)
+ - `mtls` - Mutual TLS or X.509 certificate validation
+ - Any other value will POST the client id and secret in the request body
+ - If not specified, defaults to `basic`.
+ - `<uid_field>` (optional) is the field name from the `user_info` details that will be used as `uid` value. For example, `preferred_username`.
+ If this value is not provided or the field with the configured value is missing from the `user_info` details, the `uid` will use the `sub` field.
+ - `client_options` are the OpenID Connect client-specific options. Specifically:
+ - `identifier` is the client identifier as configured in the OpenID Connect service provider.
+ - `secret` is the client secret as configured in the OpenID Connect service provider.
+ - `redirect_uri` is the GitLab URL to redirect the user to after successful login. For example, `http://example.com/users/auth/openid_connect/callback`.
+ - `end_session_endpoint` (optional) is the URL to the endpoint that end the session (logout). Can be provided if auto-discovery disabled or unsuccessful.
+ - The following `client_options` are optional unless auto-discovery is disabled or unsuccessful:
+ - `authorization_endpoint` is the URL to the endpoint that authorizes the end user.
+ - `token_endpoint` is the URL to the endpoint that provides Access Token.
+ - `userinfo_endpoint` is the URL to the endpoint that provides the user information.
+ - `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys.
+
+1. Save the configuration file.
+1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
+ 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 an OpenID Connect icon below the regular sign in form.
Click the icon to begin the authentication process. The OpenID Connect provider will ask the user to
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 3e3054af509..e418938451a 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -27,8 +27,6 @@ but not recommended and out of the scope of this document.
Read the [insecure Registry documentation][docker-insecure] if you want to
implement this.
----
-
**Installations from source**
If you have installed GitLab from source:
@@ -116,8 +114,6 @@ GitLab from source respectively.
Be careful to choose a port different than the one that Registry listens to (`5000` by default),
otherwise you will run into conflicts.
----
-
**Omnibus GitLab installations**
1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
@@ -141,8 +137,6 @@ otherwise you will run into conflicts.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
@@ -158,8 +152,6 @@ otherwise you will run into conflicts.
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).
----
-
Users should now be able to login to the Container Registry with their GitLab
credentials using:
@@ -177,8 +169,6 @@ domain (e.g., `registry.gitlab.example.com`).
Let's assume that you want the container Registry to be accessible at
`https://registry.gitlab.example.com`.
----
-
**Omnibus GitLab installations**
1. Place your TLS certificate and key in
@@ -210,8 +200,6 @@ look like:
> registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
> ```
----
-
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
@@ -226,8 +214,6 @@ look like:
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).
----
-
Users should now be able to login to the Container Registry using their GitLab
credentials:
@@ -252,8 +238,6 @@ Registry application itself.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
@@ -272,8 +256,6 @@ If the Container Registry is enabled, then it will be available on all new
projects. To disable this function and let the owners of a project to enable
the Container Registry by themselves, follow the steps below.
----
-
**Omnibus GitLab installations**
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
@@ -284,8 +266,6 @@ the Container Registry by themselves, follow the steps below.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `default_projects_features`
@@ -321,8 +301,6 @@ This path is accessible to:
> **Warning** You should confirm that all GitLab, Registry and web server users
have access to this directory.
----
-
**Omnibus GitLab installations**
The default location where images are stored in Omnibus, is
@@ -336,8 +314,6 @@ The default location where images are stored in Omnibus, is
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
The default location where images are stored in source installations, is
@@ -446,8 +422,6 @@ In the examples below we set the Registry's port to `5001`.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
1. Open the configuration file of your Registry server and edit the
@@ -565,8 +539,6 @@ To configure a notification endpoint in Omnibus:
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**Installations from source**
Configuring the notification endpoint is done in your registry config YML file created
@@ -640,8 +612,6 @@ Start with a value between `25000000` (25MB) and `50000000` (50MB).
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**For installations from source**
1. Edit `config/gitlab.yml`:
@@ -673,8 +643,6 @@ You can add a configuration option for backwards compatibility.
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
----
-
**For installations from source**
1. Edit the YML configuration file you created when you [deployed the registry][registry-deploy]. Add the following snippet:
diff --git a/doc/administration/geo/replication/index.md b/doc/administration/geo/replication/index.md
index dc49a95e008..5aedb665935 100644
--- a/doc/administration/geo/replication/index.md
+++ b/doc/administration/geo/replication/index.md
@@ -14,14 +14,8 @@ Fetching large repositories can take a long time for teams located far from a si
Geo provides local, read-only instances of your GitLab instances, reducing the time it takes to clone and fetch large repositories and speeding up development.
-> **Notes:**
->
-> - Geo is part of [GitLab Premium](https://about.gitlab.com/pricing/#self-managed).
-> - We recommend you use:
-> - At least GitLab Enterprise Edition 10.0 for basic Geo features.
-> - The latest version for a better experience.
-> - Make sure that all nodes run the same GitLab version.
-> - Geo requires PostgreSQL 9.6 and Git 2.9, in addition to GitLab's usual [minimum requirements](../../../install/requirements.md).
+NOTE: **Note:**
+Check the [requirements](#requirements-for-running-geo) carefully before setting up Geo.
For a video introduction to Geo, see [Introduction to GitLab Geo - GitLab Features](https://www.youtube.com/watch?v=-HDLxSjEh6w).
@@ -117,6 +111,13 @@ The following are required to run Geo:
- [Ubuntu](https://www.ubuntu.com) 16.04+
- PostgreSQL 9.6+ with [FDW](https://www.postgresql.org/docs/9.6/postgres-fdw.html) support and [Streaming Replication](https://wiki.postgresql.org/wiki/Streaming_Replication)
- Git 2.9+
+- All nodes must run the same GitLab version.
+
+Additionally, check GitLab's [minimum requirements](../../../install/requirements.md),
+and we recommend you use:
+
+- At least GitLab Enterprise Edition 10.0 for basic Geo features.
+- The latest version for a better experience.
### Firewall rules
diff --git a/doc/administration/high_availability/pgbouncer.md b/doc/administration/high_availability/pgbouncer.md
index 0b945bc6244..b99724d12a2 100644
--- a/doc/administration/high_availability/pgbouncer.md
+++ b/doc/administration/high_availability/pgbouncer.md
@@ -20,84 +20,85 @@ It is recommended to run pgbouncer alongside the `gitlab-rails` service, or on i
1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
- ```ruby
- # Disable all components except Pgbouncer and Consul agent
- roles ['pgbouncer_role']
-
- # Configure Pgbouncer
- pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
-
- # Configure Consul agent
- consul['watchers'] = %w(postgresql)
-
- # START user configuration
- # Please set the real values as explained in Required Information section
- # Replace CONSUL_PASSWORD_HASH with with a generated md5 value
- # Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value
- pgbouncer['users'] = {
- 'gitlab-consul': {
- password: 'CONSUL_PASSWORD_HASH'
- },
- 'pgbouncer': {
- password: 'PGBOUNCER_PASSWORD_HASH'
- }
- }
- # Replace placeholders:
- #
- # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
- # with the addresses gathered for CONSUL_SERVER_NODES
- consul['configuration'] = {
- retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
- }
- #
- # END user configuration
- ```
-
- > `pgbouncer_role` was introduced with GitLab 10.3
-
-1. Run `gitlab-ctl reconfigure`
+ ```ruby
+ # Disable all components except Pgbouncer and Consul agent
+ roles ['pgbouncer_role']
+
+ # Configure Pgbouncer
+ pgbouncer['admin_users'] = %w(pgbouncer gitlab-consul)
+
+ # Configure Consul agent
+ consul['watchers'] = %w(postgresql)
+
+ # START user configuration
+ # Please set the real values as explained in Required Information section
+ # Replace CONSUL_PASSWORD_HASH with with a generated md5 value
+ # Replace PGBOUNCER_PASSWORD_HASH with with a generated md5 value
+ pgbouncer['users'] = {
+ 'gitlab-consul': {
+ password: 'CONSUL_PASSWORD_HASH'
+ },
+ 'pgbouncer': {
+ password: 'PGBOUNCER_PASSWORD_HASH'
+ }
+ }
+ # Replace placeholders:
+ #
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses gathered for CONSUL_SERVER_NODES
+ consul['configuration'] = {
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z)
+ }
+ #
+ # END user configuration
+ ```
+
+ NOTE: **Note:**
+ `pgbouncer_role` was introduced with GitLab 10.3.
+
+1. Run `gitlab-ctl reconfigure`
1. Create a `.pgpass` file so Consul is able to
reload pgbouncer. Enter the `PGBOUNCER_PASSWORD` twice when asked:
- ```sh
- gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
- ```
+ ```sh
+ gitlab-ctl write-pgpass --host 127.0.0.1 --database pgbouncer --user pgbouncer --hostuser gitlab-consul
+ ```
#### PGBouncer Checkpoint
1. Ensure the node is talking to the current master:
- ```sh
- gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
- ```
+ ```sh
+ gitlab-ctl pgb-console # You will be prompted for PGBOUNCER_PASSWORD
+ ```
- If there is an error `psql: ERROR: Auth failed` after typing in the
- password, ensure you previously generated the MD5 password hashes with the correct
- format. The correct format is to concatenate the password and the username:
- `PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
- needed to generate an MD5 password hash for the `pgbouncer` user.
+ If there is an error `psql: ERROR: Auth failed` after typing in the
+ password, ensure you previously generated the MD5 password hashes with the correct
+ format. The correct format is to concatenate the password and the username:
+ `PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text
+ needed to generate an MD5 password hash for the `pgbouncer` user.
1. Once the console prompt is available, run the following queries:
- ```sh
- show databases ; show clients ;
- ```
-
- The output should be similar to the following:
+ ```sh
+ show databases ; show clients ;
+ ```
- ```
- name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
- ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
- gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
- pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
- (2 rows)
+ The output should be similar to the following:
- type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
- ------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
- C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
- (2 rows)
- ```
+ ```
+ name | host | port | database | force_user | pool_size | reserve_pool | pool_mode | max_connections | current_connections
+ ---------------------+-------------+------+---------------------+------------+-----------+--------------+-----------+-----------------+---------------------
+ gitlabhq_production | MASTER_HOST | 5432 | gitlabhq_production | | 20 | 0 | | 0 | 0
+ pgbouncer | | 6432 | pgbouncer | pgbouncer | 2 | 0 | statement | 0 | 0
+ (2 rows)
+
+ type | user | database | state | addr | port | local_addr | local_port | connect_time | request_time | ptr | link | remote_pid | tls
+ ------+-----------+---------------------+---------+----------------+-------+------------+------------+---------------------+---------------------+-----------+------+------------+-----
+ C | pgbouncer | pgbouncer | active | 127.0.0.1 | 56846 | 127.0.0.1 | 6432 | 2017-08-21 18:09:59 | 2017-08-21 18:10:48 | 0x22b3880 | | 0 |
+ (2 rows)
+ ```
### Running Pgbouncer as part of a non-HA GitLab installation
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 91a4d5097f2..2b25e8efd23 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -2,7 +2,7 @@
description: 'Learn how to install, configure, update, and maintain your GitLab instance.'
---
-# Administrator documentation **(CORE ONLY)**
+# Administrator Docs **(CORE ONLY)**
Learn how to administer your self-managed GitLab instance.
@@ -190,3 +190,8 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Troubleshooting ElasticSearch](troubleshooting/elasticsearch.md): Tips to troubleshoot ElasticSearch.
- [Kubernetes troubleshooting](troubleshooting/kubernetes_cheat_sheet.md): Commands and tips useful
for troubleshooting Kubernetes-related issues.
+- Useful links from the Support Team:
+ - [GitLab Developer Docs](https://docs.gitlab.com/ee/development/README.html).
+ - [Repairing and recovering broken git repositories](https://git.seveas.net/repairing-and-recovering-broken-git-repositories.html).
+ - [Testing with OpenSSL](https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html).
+ - [Strace zine](https://wizardzines.com/zines/strace/).
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index 6778c339922..95be0d5fd88 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -118,6 +118,36 @@ If you have set up Grafana, you can enable a link to access it easily from the s
1. Click **Save changes**.
1. The new link will be available in the admin area under **Monitoring > Metrics Dashboard**.
+## Security Update
+
+Users running GitLab version 12.0 or later should immediately upgrade to one of the following security releases due to a known vulnerability with the embedded Grafana dashboard:
+
+- 12.0.6
+- 12.1.6
+
+After upgrading, the Grafana dashboard will be disabled and the location of your existing Grafana data will be changed from `/var/opt/gitlab/grafana/data/` to `/var/opt/gitlab/grafana/data.bak.#{Date.today}/`.
+
+To prevent the data from being relocated, you can run the following command prior to upgrading:
+
+```sh
+echo "0" > /var/opt/gitlab/grafana/CVE_reset_status
+```
+
+To reinstate your old data, move it back into its original location:
+
+```
+sudo mv /var/opt/gitlab/grafana/data.bak.xxxx/ /var/opt/gitlab/grafana/data/
+```
+
+However, you should **not** reinstate your old data _except_ under one of the following conditions:
+
+1. If you are certain that you changed your default admin password when you enabled Grafana
+1. If you run GitLab in a private network, accessed only by trusted users, and your Grafana login page has not been exposed to the internet
+
+If you require access to your old Grafana data but do not meet one of these criteria, you may consider reinstating it temporarily, [exporting the dashboards](https://grafana.com/docs/reference/export_import/#exporting-a-dashboard) you need, then refreshing the data and [re-importing your dashboards](https://grafana.com/docs/reference/export_import/#importing-a-dashboard). Note that this poses a temporary vulnerability while your old Grafana data is in use, and the decision to do so should be weighed carefully with your need to access existing data and dashboards.
+
+For more information and further mitigation details, please refer to our [blog post on the security release](https://about.gitlab.com/2019/08/12/critical-security-release-gitlab-12-dot-1-dot-6-released/).
+
---
Read more on:
diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md
index ae67d7f08d6..8178cb243f3 100644
--- a/doc/administration/operations/unicorn.md
+++ b/doc/administration/operations/unicorn.md
@@ -69,7 +69,7 @@ unicorn['worker_memory_limit_min'] = "400 * 1 << 20"
unicorn['worker_memory_limit_max'] = "650 * 1 << 20"
```
-Otherwise, you can set the `GITLAB_UNICORN_MEMORY_MIN` and `GITLAB_UNICORN_MEMORY_MIN`
+Otherwise, you can set the `GITLAB_UNICORN_MEMORY_MIN` and `GITLAB_UNICORN_MEMORY_MAX`
[environment variables](../environment_variables.md).
This is what a Unicorn worker memory restart looks like in unicorn_stderr.log.
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index b2cad6cf926..c77a1a9638f 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -24,8 +24,6 @@ SNI and exposes pages using HTTP2 by default.
You are encouraged to read its [README][pages-readme] to fully understand how
it works.
----
-
In the case of [custom domains](#custom-domains) (but not
[wildcard domains](#wildcard-domains)), the Pages daemon needs to listen on
ports `80` and/or `443`. For that reason, there is some flexibility in the way
@@ -92,8 +90,6 @@ since that is needed in all configurations.
- [Wildcard DNS setup](#dns-configuration)
----
-
URL scheme: `http://page.example.io`
This is the minimum setup that you can use Pages with. It is the base for all
@@ -152,13 +148,12 @@ The Pages daemon doesn't listen to the outside world.
### Wildcard domains with TLS support
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Wildcard TLS certificate
->
-> ---
->
-> URL scheme: `https://page.example.io`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Wildcard TLS certificate
+
+URL scheme: `https://page.example.io`
Nginx will proxy all requests to the daemon. Pages daemon doesn't listen to the
outside world.
@@ -217,13 +212,12 @@ that without TLS certificates.
### Custom domains
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Secondary IP
->
-> ---
->
-> URL scheme: `http://page.example.io` and `http://domain.com`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Secondary IP
+
+URL scheme: `http://page.example.io` and `http://domain.com`
In that case, the pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
@@ -282,14 +276,13 @@ world. Custom domains are supported, but no TLS.
### Custom domains with TLS support
-> **Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
-> - Wildcard TLS certificate
-> - Secondary IP
->
-> ---
->
-> URL scheme: `https://page.example.io` and `https://domain.com`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+- Wildcard TLS certificate
+- Secondary IP
+
+URL scheme: `https://page.example.io` and `https://domain.com`
In that case, the pages daemon is running, Nginx still proxies requests to
the daemon but the daemon is also able to receive requests from the outside
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index 2f83dd17d9f..1198f3414c5 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -105,8 +105,6 @@ sudo gitlab-rake gitlab:storage:legacy_projects
sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production
```
----
-
To list projects using **Legacy** storage:
**Omnibus Installation**
@@ -138,8 +136,6 @@ sudo gitlab-rake gitlab:storage:hashed_projects
sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production
```
----
-
To list projects using **Hashed** storage:
**Omnibus Installation**
@@ -170,8 +166,6 @@ sudo gitlab-rake gitlab:storage:legacy_attachments
sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production
```
----
-
To list project attachments using **Legacy** storage:
**Omnibus Installation**
@@ -202,8 +196,6 @@ sudo gitlab-rake gitlab:storage:hashed_attachments
sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production
```
----
-
To list project attachments using **Hashed** storage:
**Omnibus Installation**
diff --git a/doc/api/README.md b/doc/api/README.md
index 0195ce3912a..b7ee710b87a 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -1,4 +1,4 @@
-# GitLab API
+# API Docs
Automate GitLab via a simple and powerful API.
@@ -6,7 +6,7 @@ The main GitLab API is a [REST](https://en.wikipedia.org/wiki/Representational_s
## Available API resources
-For a list of the available resources and their endpoints, see
+For a list of the available resources and their endpoints, see
[API resources](api_resources.md).
## SCIM **(SILVER ONLY)**
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index ac64cbedf7d..e3af5d60533 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -192,6 +192,10 @@ Example response:
"job_artifacts_failed_count": nil,
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "0.00%",
+ "container_repositories_count": 3,
+ "container_repositories_synced_count": nil,
+ "container_repositories_failed_count": nil,
+ "container_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_failed_count": nil,
"repositories_synced_count": nil,
@@ -255,6 +259,10 @@ Example response:
"job_artifacts_failed_count": 1,
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "50.00%",
+ "container_repositories_count": 3,
+ "container_repositories_synced_count": nil,
+ "container_repositories_failed_count": nil,
+ "container_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_failed_count": 1,
"repositories_synced_count": 40,
@@ -334,6 +342,10 @@ Example response:
"job_artifacts_failed_count": 1,
"job_artifacts_synced_missing_on_primary_count": 0,
"job_artifacts_synced_in_percentage": "50.00%",
+ "container_repositories_count": 3,
+ "container_repositories_synced_count": nil,
+ "container_repositories_failed_count": nil,
+ "container_repositories_synced_in_percentage": "0.00%",
"projects_count": 41,
"repositories_failed_count": 1,
"repositories_synced_count": 40,
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 5b2c3a8765c..f8151e3e18c 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -378,8 +378,8 @@ Here's what happens behind the scenes:
1. `script` is executed.
1. `after_script` is executed.
1. `cache` runs and the `vendor/` directory is zipped into `cache.zip`.
- This file is then saved in the directory based on the
- [Runner's setting](#where-the-caches-are-stored) and the `cache: key`.
+ This file is then saved in the directory based on the
+ [Runner's setting](#where-the-caches-are-stored) and the `cache: key`.
1. `job B` runs.
1. The cache is extracted (if found).
1. `before_script` is executed.
@@ -520,7 +520,7 @@ via GitLab's UI:
1. Navigate to your project's **CI/CD > Pipelines** page.
1. Click on the **Clear Runner caches** button to clean up the cache.
- ![Clear Runners cache](img/clear_runners_cache.png)
+ ![Clear Runners cache](img/clear_runners_cache.png)
1. On the next push, your CI/CD job will use a new cache.
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
index 241134783da..29d4f93f02e 100644
--- a/doc/ci/chatops/README.md
+++ b/doc/ci/chatops/README.md
@@ -4,14 +4,14 @@ type: index, concepts, howto
# GitLab ChatOps
-> **Notes:**
->
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
->
-> - ChatOps is currently in alpha, with some important features missing like access control.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
+NOTE: **Note:**
+ChatOps is currently in alpha with some important features missing, like access control.
+
## How it works
GitLab ChatOps is built upon two existing features:
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index 8fe11140cc7..54b21939116 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -44,7 +44,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
Passwords** to authenticate the build status script setting commit build
statuses in Bitbucket. Repository write permissions are required.
- ![Bitbucket Cloud webhook](img/bitbucket_app_password.png)
+ ![Bitbucket Cloud webhook](img/bitbucket_app_password.png)
1. In GitLab, from **Settings > CI/CD > Environment variables**, add variables to allow
communication with Bitbucket via the Bitbucket API:
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index 0bb3aa35ed0..f639e3dadee 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -19,12 +19,12 @@ administrator:
1. In GitLab create a **CI/CD for external repo** project and select
**GitHub**.
- ![Create project](img/github_omniauth.png)
+ ![Create project](img/github_omniauth.png)
1. Once authenticated, you will be redirected to a list of your repositories to
connect. Click **Connect** to select the repository.
- ![Create project](img/github_repo_list.png)
+ ![Create project](img/github_repo_list.png)
1. In GitHub, add a `.gitlab-ci.yml` to [configure GitLab CI/CD](../quick_start/README.md).
@@ -55,14 +55,14 @@ repositories:
Token**. This token with be used to access your repository and push commit
statuses to GitHub.
- The `repo` and `admin:repo_hook` should be enable to allow GitLab access to
- your project, update commit statuses, and create a web hook to notify
- GitLab of new commits.
+ The `repo` and `admin:repo_hook` should be enable to allow GitLab access to
+ your project, update commit statuses, and create a web hook to notify
+ GitLab of new commits.
1. In GitLab create a **CI/CD for external repo** project and select
**GitHub**.
- ![Create project](img/github_omniauth.png)
+ ![Create project](img/github_omniauth.png)
1. Paste the token into the **Personal access token** field and click **List
Repositories**. Click **Connect** to select the repository.
@@ -86,21 +86,21 @@ your repository:
Access Token.** GitLab will use this token to access your repository and
push commit statuses.
- Enter a **Token description** and update the scope to allow:
+ Enter a **Token description** and update the scope to allow:
- `repo` so that GitLab can access your project and update commit statuses
+ `repo` so that GitLab can access your project and update commit statuses
1. In GitLab create a **CI/CD project** using the Git URL option and the HTTPS
URL for your GitHub repository. If your project is private, use the personal
access token you just created for authentication.
- GitLab will automatically configure polling-based pull mirroring.
+ GitLab will automatically configure polling-based pull mirroring.
1. Still in GitLab, enable the [GitHub project integration](../../user/project/integrations/github.md)
from **Settings > Integrations.**
- Check the **Active** checkbox to enable the integration, paste your
- personal access token and HTTPS repository URL into the form, and **Save.**
+ Check the **Active** checkbox to enable the integration, paste your
+ personal access token and HTTPS repository URL into the form, and **Save.**
1. Still in GitLab create a **Personal Access Token** with `API` scope to
authenticate the GitHub web hook notifying GitLab of new commits.
@@ -108,15 +108,15 @@ your repository:
1. In GitHub from **Settings > Webhooks** create a web hook to notify GitLab of
new commits.
- The web hook URL should be set to the GitLab API to
- [trigger pull mirroring](../../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter),
- using the GitLab personal access token we just created.
+ The web hook URL should be set to the GitLab API to
+ [trigger pull mirroring](../../api/projects.md#start-the-pull-mirroring-process-for-a-project-starter),
+ using the GitLab personal access token we just created.
- ```
- https://gitlab.com/api/v4/projects/<NAMESPACE>%2F<PROJECT>/mirror/pull?private_token=<PERSONAL_ACCESS_TOKEN>
- ```
+ ```
+ https://gitlab.com/api/v4/projects/<NAMESPACE>%2F<PROJECT>/mirror/pull?private_token=<PERSONAL_ACCESS_TOKEN>
+ ```
- ![Create web hook](img/github_push_webhook.png)
+ ![Create web hook](img/github_push_webhook.png)
1. In GitHub add a `.gitlab-ci.yml` to configure GitLab CI/CD.
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 61559e69182..9e1a62bae71 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -591,15 +591,13 @@ exist, you should see something like:
### Monitoring environments
-> **Notes:**
->
-> - For the monitoring dashboard to appear, you need to:
-> - Enable the [Prometheus integration](../user/project/integrations/prometheus.md).
-> - Configure Prometheus to collect at least one [supported metric](../user/project/integrations/prometheus_library/index.md).
-> - With GitLab 9.2, all deployments to an environment are shown directly on the monitoring dashboard.
-
If you have enabled [Prometheus for monitoring system and response metrics](../user/project/integrations/prometheus.md),
-you can monitor the behavior of your app running in each environment.
+you can monitor the behavior of your app running in each environment. For the monitoring
+dashboard to appear, you need to Configure Prometheus to collect at least one
+[supported metric](../user/project/integrations/prometheus_library/index.md).
+
+NOTE: **Note:**
+Since GitLab 9.2, all deployments to an environment are shown directly on the monitoring dashboard.
Once configured, GitLab will attempt to retrieve [supported performance metrics](../user/project/integrations/prometheus_library/index.md)
for any environment that has had a successful deployment. If monitoring data was
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 940c4711132..e5f307e570d 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -1,5 +1,5 @@
---
-redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
+disqus_identifier: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
author: Fabio Busatto
author_gitlab: bikebilly
level: intermediate
diff --git a/doc/ci/examples/code_quality.md b/doc/ci/examples/code_quality.md
index 69bad6b4c25..9c65de115b4 100644
--- a/doc/ci/examples/code_quality.md
+++ b/doc/ci/examples/code_quality.md
@@ -1,5 +1,5 @@
---
-redirect_from: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
+disqus_identifier: 'https://docs.gitlab.com/ee/ci/examples/code_climate.html'
type: reference, howto
---
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index 50e61cafeb9..44d3ec8046c 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -418,10 +418,14 @@ fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/lat
1. Log into your AWS account and go to the [Security Credentials page](https://console.aws.amazon.com/iam/home#/security_credential)
1. Click the **Access Keys** section and **Create New Access Key**. Create the key and keep the id and secret around, you'll need them later
- ![AWS Access Key Config](img/aws_config_window.png)
+
+ ![AWS Access Key Config](img/aws_config_window.png)
+
1. Go to your GitLab project, click **Settings > CI/CD** on the left sidebar
1. Expand the **Variables** section
- ![GitLab Secret Config](img/gitlab_config.png)
+
+ ![GitLab Secret Config](img/gitlab_config.png)
+
1. Add a key named `AWS_KEY_ID` and copy the key id from Step 2 into the **Value** textbox
1. Add a key named `AWS_KEY_SECRET` and copy the key secret from Step 2 into the **Value** textbox
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index 1576efd5a7d..808e4285f2f 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -1,5 +1,5 @@
---
-redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
+disqus_identifier: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
author: Mehran Rasulian
author_gitlab: mehranrasulian
level: intermediate
diff --git a/doc/ci/jenkins/index.md b/doc/ci/jenkins/index.md
index 093b3568a78..f8a3fab88e3 100644
--- a/doc/ci/jenkins/index.md
+++ b/doc/ci/jenkins/index.md
@@ -30,6 +30,34 @@ There are some high level differences between the products worth mentioning:
- GitLab comes with a [container registry](../../user/project/container_registry.md), and we recommend using
container images to set up your build environment.
+## Groovy vs. YAML
+
+Jenkins Pipelines are based on [Groovy](https://groovy-lang.org/), so the pipeline specification is written as code.
+GitLab works a bit differently, we use the more highly structured [YAML](https://yaml.org/) format, which
+places scripting elements inside of `script:` blocks separate from the pipeline specification itself.
+
+This is a strength of GitLab, in that it helps keep the learning curve much simpler to get up and running
+and avoids some of the problem of unconstrained complexity which can make your Jenkinsfiles hard to understand
+and manage.
+
+That said, we do of course still value DRY (don't repeat yourself) principles and want to ensure that
+behaviors of your jobs can be codified once and applied as needed. You can use the `extends:` syntax to
+[templatize your jobs](../yaml/README.md#extends), and `include:` can be used to [bring in entire sets of behaviors](../yaml/README.md#include)
+to pipelines in different projects.
+
+```yaml
+.in-docker:
+ tags:
+ - docker
+ image: alpine
+
+rspec:
+ extends:
+ - .in-docker
+ script:
+ - rake rspec
+```
+
## Artifact publishing
Artifacts may work a bit differently than you've used them with Jenkins. In GitLab, any job can define
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index 9b89988bf42..8ab7982fd65 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -147,11 +147,11 @@ Once you have the route mapping set up, it will take effect in the following loc
- In the diff for a merge request, comparison, or commit.
- !["View on env" button in merge request diff](img/view_on_env_mr.png)
+ !["View on env" button in merge request diff](img/view_on_env_mr.png)
- In the blob file view.
- !["View on env" button in file view](img/view_on_env_blob.png)
+ !["View on env" button in file view](img/view_on_env_blob.png)
## Visual Reviews **(STARTER)**
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
index 03a219e03ca..c3678fc948e 100644
--- a/doc/ci/runners/README.md
+++ b/doc/ci/runners/README.md
@@ -62,7 +62,7 @@ You can only register a shared Runner if you are an admin of the GitLab instance
1. Grab the shared-Runner token on the `admin/runners` page
- ![Shared Runners admin area](img/shared_runners_admin.png)
+ ![Shared Runners admin area](img/shared_runners_admin.png)
1. [Register the Runner][register]
@@ -373,12 +373,12 @@ attacker.
To reset the token:
-1. Go to **Settings > CI/CD** for a specified Project
-1. Expand the **General pipelines settings** section
-1. Find the **Runner token** form field and click the **Reveal value** button
-1. Delete the value and save the form
+1. Go to **Settings > CI/CD** for a specified Project.
+1. Expand the **General pipelines settings** section.
+1. Find the **Runner token** form field and click the **Reveal value** button.
+1. Delete the value and save the form.
1. After the page is refreshed, expand the **Runners settings** section
- and check the registration token - it should be changed
+ and check the registration token - it should be changed.
From now on the old token is not valid anymore and will not allow to register
a new Runner to the project. If you are using any tools to provision and
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 49543c57886..409f7d62538 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -30,7 +30,7 @@ future GitLab releases.**
| `CI_BUILDS_DIR` | all | 11.10 | Top-level directory where builds are executed. |
| `CI_CONCURRENT_ID` | all | 11.10 | Unique ID of build execution within a single executor. |
| `CI_CONCURRENT_PROJECT_ID` | all | 11.10 | Unique ID of build execution within a single executor and project. |
-| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch before a push request. Only populated when there is a merge request associated with the pipeline. |
+| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch before a merge request. Only populated when there is a merge request associated with the pipeline. |
| `CI_COMMIT_DESCRIPTION` | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. |
| `CI_COMMIT_MESSAGE` | 10.8 | all | The full commit message. |
| `CI_COMMIT_REF_NAME` | 9.0 | all | The branch or tag name for which project is built |
diff --git a/doc/development/README.md b/doc/development/README.md
index 44283a3ab0c..6281bb809ff 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -3,7 +3,7 @@ comments: false
description: 'Learn how to contribute to GitLab.'
---
-# GitLab development guides
+# Contributor and Development Docs
## Get started!
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 17328762c5b..4e9c5c81379 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -143,6 +143,37 @@ If the guidelines are not met, the MR will not pass the
[Danger checks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/danger/commit_messages/Dangerfile).
For more information see [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
+Example commit message template that can be used on your machine that embodies the above (guide for [how to apply template](https://codeinthehole.com/tips/a-useful-template-for-commit-messages/)):
+
+```text
+# (If applied, this commit will...) <subject> (Max 50 char)
+# |<---- Using a Maximum Of 50 Characters ---->|
+
+
+# Explain why this change is being made
+# |<---- Try To Limit Each Line to a Maximum Of 72 Characters ---->|
+
+# Provide links or keys to any relevant tickets, articles or other resources
+# Use issues and merge requests' full URLs instead of short references,
+# as they are displayed as plain text outside of GitLab
+
+# --- COMMIT END ---
+# --------------------
+# Remember to
+# Capitalize the subject line
+# Use the imperative mood in the subject line
+# Do not end the subject line with a period
+# Subject must contain at least 3 words
+# Separate subject from body with a blank line
+# Commits that change 30 or more lines across at least 3 files must
+# describe these changes in the commit body
+# Do not use Emojis
+# Use the body to explain what and why vs. how
+# Can use multiple lines with "-" for bullet points in body
+# For more information: https://chris.beams.io/posts/git-commit/
+# --------------------
+```
+
## Contribution acceptance criteria
To make sure that your merge request can be approved, please ensure that it meets
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 19b26618882..c9ae00d148a 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -170,7 +170,7 @@ Disqus uses an identifier per page, and for docs.gitlab.com, the page identifier
is configured to be the page URL. Therefore, when we change the document location,
we need to preserve the old URL as the same Disqus identifier.
-To do that, add to the frontmatter the variable `redirect_from`,
+To do that, add to the frontmatter the variable `disqus_identifier`,
using the old URL as value. For example, let's say I moved the document
available under `https://docs.gitlab.com/my-old-location/README.html` to a new location,
`https://docs.gitlab.com/my-new-location/index.html`.
@@ -179,11 +179,11 @@ Into the **new document** frontmatter add the following:
```yaml
---
-redirect_from: 'https://docs.gitlab.com/my-old-location/README.html'
+disqus_identifier: 'https://docs.gitlab.com/my-old-location/README.html'
---
```
-Note: it is necessary to include the file name in the `redirect_from` URL,
+Note: it is necessary to include the file name in the `disqus_identifier` URL,
even if it's `index.html` or `README.html`.
## Branch naming
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index e84d65f424e..59c8bfe2964 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -41,7 +41,7 @@ Include any media types/sources if the content is relevant to readers. You can f
### No special types
-In the software industry, it is a best practice to organize documentatioin in different types. For example, [Divio recommends](https://www.divio.com/blog/documentation/):
+In the software industry, it is a best practice to organize documentation in different types. For example, [Divio recommends](https://www.divio.com/blog/documentation/):
1. Tutorials
1. How-to guides
@@ -283,24 +283,44 @@ Check specific punctuation rules for [list items](#list-items) below.
## List items
-- Always start list items with a capital letter.
+- Always start list items with a capital letter, unless they are parameters or commands
+ that are in backticks, or similar.
- Always leave a blank line before and after a list.
-- Begin a line with spaces (not tabs) to denote a subitem.
-- To nest subitems, indent them with two spaces.
-- To nest code blocks, indent them with four spaces.
-- Only use ordered lists when their items describe a sequence of steps to follow.
+- Begin a line with spaces (not tabs) to denote a [nested subitem](#nesting-inside-a-list-item).
+- Only use ordered lists when their items describe a sequence of steps to follow:
+
+ Do:
+
+ These are the steps to do something:
+
+ 1. First, do step 1
+ 1. Then, do step 2
+ 1. Finally, do step 3
+
+ Don't:
+
+ This is a list of different features:
+
+ 1. Feature 1
+ 1. Feature 2
+ 1. Feature 3
**Markup:**
- Use dashes (`-`) for unordered lists instead of asterisks (`*`).
-- Use the number one (`1`) for each item in an ordered list.
- When rendered, the list items will appear with sequential numbering.
+- Prefix `1.` to each item in an ordered list.
+ When rendered, the list items will appear with sequential numbering automatically.
**Punctuation:**
-- Do not add commas (`,`) or semicolons (`;`) to the end of a list item.
-- Only add periods to the end of a list item if the item consists of a complete sentence. The [definition of full sentence](https://www2.le.ac.uk/offices/ld/resources/writing/grammar/grammar-guides/sentence) is: _"a complete sentence always contains a verb, expresses a complete idea, and makes sense standing alone"_.
-- Be consistent throughout the list: if the majority of the items do not end in a period, do not end any of the items in a period, even if they consist of a complete sentence. The opposite is also valid: if the majority of the items end with a period, end all with a period.
+- Do not add commas (`,`) or semicolons (`;`) to the end of list items.
+- Only add periods to the end of a list item if the item consists of a complete sentence.
+ The [definition of full sentence](https://www2.le.ac.uk/offices/ld/resources/writing/grammar/grammar-guides/sentence)
+ is: _"a complete sentence always contains a verb, expresses a complete idea, and makes sense standing alone"_.
+- Be consistent throughout the list: if the majority of the items do not end in a period,
+ do not end any of the items in a period, even if they consist of a complete sentence.
+ The opposite is also valid: if the majority of the items end with a period, end
+ all with a period.
- Separate list items from explanatory text with a colon (`:`). For example:
```md
@@ -330,12 +350,86 @@ Do:
- Let's say this is also a complete sentence.
- Not a complete sentence.
-Don't:
+Don't (third item should have a `.` to match the first and second items):
- Let's say this is a complete sentence.
- Let's say this is also a complete sentence.
- Not a complete sentence
+### Nesting inside a list item
+
+It is possible to nest items under a list item, so that they render with the same indentation
+as the list item. This can be done with:
+
+- [Code blocks](#code-blocks)
+- [Blockquotes](#blockquotes)
+- [Alert boxes](#alert-boxes)
+- [Images](#images)
+
+Items nested in lists should always align with the first character of the list item.
+In unordered lists (using `-`), this means two spaces for each level of indentation:
+
+~~~md
+- Unordered list item 1
+
+ A line nested using 2 spaces to align with the `U` above.
+
+- Unordered list item 2
+
+ > A quote block that will nest
+ > inside list item 2.
+
+- Unordered list item 3
+
+ ```text
+ a codeblock that will next inside list item 3
+ ```
+
+- Unordered list item 4
+
+ ![an image that will nest inside list item 4](image.png)
+~~~
+
+For ordered lists, use three spaces for each level of indentation:
+
+~~~md
+1. Ordered list item 1
+
+ A line nested using 3 spaces to align with the `O` above.
+
+1. Ordered list item 2
+
+ > A quote block that will nest
+ > inside list item 2.
+
+1. Ordered list item 3
+
+ ```text
+ a codeblock that will next inside list item 3
+ ```
+
+1. Ordered list item 4
+
+ ![an image that will nest inside list item 4](image.png)
+~~~
+
+You can nest full lists inside other lists using the same rules as above. If you wish
+to mix types, that is also possible, as long as you don't mix items at the same level:
+
+```
+1. Ordered list item one.
+1. Ordered list item two.
+ - Nested unordered list item one.
+ - Nested unordered list item two.
+1. Ordered list item three.
+
+- Unordered list item one.
+- Unordered list item two.
+ 1. Nested ordered list item one.
+ 1. Nested ordered list item two.
+- Unordered list item three.
+```
+
## Quotes
Valid for markdown content only, not for frontmatter entries:
diff --git a/doc/development/fe_guide/event_tracking.md b/doc/development/fe_guide/event_tracking.md
index 1e6287d8f6d..715d91c6db6 100644
--- a/doc/development/fe_guide/event_tracking.md
+++ b/doc/development/fe_guide/event_tracking.md
@@ -40,10 +40,9 @@ Below is a list of supported `data-track-*` attributes:
| attribute | required | description |
|:----------------------|:---------|:------------|
| `data-track-event` | true | Action the user is taking. Clicks should be `click` and activations should be `activate`, so for example, focusing a form field would be `activate_form_input`, and clicking a button would be `click_button`. |
-| `data-track-label` | false | The `label` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy) |
-| `data-track-property` | false | The `property` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy)
-| `data-track-value` | false | The `value` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). If omitted, this will be the elements `value` property or an empty string. For checkboxes, the default value will be the element's checked attribute or `false` when unchecked.
-
+| `data-track-label` | false | The `label` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). |
+| `data-track-property` | false | The `property` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). |
+| `data-track-value` | false | The `value` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). If omitted, this will be the elements `value` property or an empty string. For checkboxes, the default value will be the element's checked attribute or `false` when unchecked. |
## Tracking in raw Javascript
@@ -69,7 +68,6 @@ document.getElementById('my_button').addEventListener('click', () => {
})
```
-
## Toggling tracking on or off
Snowplow can be enabled by navigating to:
diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md
index 6e6b71b1787..5e7376db725 100644
--- a/doc/development/filtering_by_label.md
+++ b/doc/development/filtering_by_label.md
@@ -35,7 +35,7 @@ LIMIT 20 OFFSET 0
In particular, note that:
1. We `GROUP BY issues.id` so that we can ...
-2. Use the `HAVING (COUNT(DISTINCT labels.title) = 2)` condition to ensure that
+1. Use the `HAVING (COUNT(DISTINCT labels.title) = 2)` condition to ensure that
all matched issues have both labels.
This is more complicated than is ideal. It makes the query construction more
@@ -103,7 +103,7 @@ This has some strong advantages over titles:
1. Unless a label is deleted, or a project is moved, we never need to
bulk-update the denormalized column.
-2. It uses less storage than the titles.
+1. It uses less storage than the titles.
Unfortunately, our application design makes this hard. If we were able to query
just by label ID easily, we wouldn't need the `INNER JOIN labels` in the initial
@@ -115,7 +115,7 @@ We do not want users to have to know about the different IDs, which means that
given this data set:
| Project | ~Plan label ID | ~backend label ID |
-| --- | --- | --- |
+| ------- | -------------- | ----------------- |
| A | 11 | 12 |
| B | 21 | 22 |
| C | 31 | 32 |
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 882e2230636..d6b944a3e74 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -83,7 +83,6 @@ subgraph "gitlab-qa pipeline"
end
```
-
1. Developer triggers a manual action, that can be found in CE / EE merge
requests. This starts a chain of pipelines in multiple projects.
diff --git a/doc/development/testing_guide/end_to_end/page_objects.md b/doc/development/testing_guide/end_to_end/page_objects.md
index 52957d1a1ab..47e58a425fd 100644
--- a/doc/development/testing_guide/end_to_end/page_objects.md
+++ b/doc/development/testing_guide/end_to_end/page_objects.md
@@ -105,7 +105,7 @@ code but **this is deprecated** in favor of the above method for two reasons:
view 'app/views/my/view.html.haml' do
### Good ###
-
+
# Implicitly require the CSS selector `[data-qa-selector="logout_button"]` to be present in the view
element :logout_button
@@ -152,10 +152,9 @@ Things to note:
- The name of the element and the qa_selector must match and be snake_cased
- If the element appears on the page unconditionally, add `required: true` to the element. See
[Dynamic element validation](dynamic_element_validation.md)
-- You may see `.qa-selector` classes in existing Page Objects. We should prefer the [`data-qa-selector`](#data-qa-selector-vs-qa-selector)
+- You may see `.qa-selector` classes in existing Page Objects. We should prefer the [`data-qa-selector`](#data-qa-selector-vs-qa-selector)
method of definition over the `.qa-selector` CSS class
-
### `data-qa-selector` vs `.qa-selector`
> Introduced in GitLab 12.1
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 086f3b1b843..83a9e7fe294 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -92,8 +92,9 @@ We recommend having at least [2GB of swap on your server](https://askubuntu.com/
enough available RAM. Having swap will help reduce the chance of errors occurring
if your available memory changes. We also recommend [configuring the kernel's swappiness setting](https://askubuntu.com/a/103916)
to a low value like `10` to make the most of your RAM while still having the swap
-available when needed.
-Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/memory/) is actively working to reduce this requirement.
+available when needed.
+
+Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/memory/) is actively working to reduce the memory requirement.
NOTE: **Note:** The 25 workers of Sidekiq will show up as separate processes in your process overview (such as `top` or `htop`) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about how many you need of those.
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
index 837434da737..49b3d194a01 100644
--- a/doc/integration/facebook.md
+++ b/doc/integration/facebook.md
@@ -19,7 +19,7 @@ To enable the Facebook OmniAuth provider you must register your application with
1. Enter the address of your GitLab installation at the bottom of the package
- ![Facebook Website URL](img/facebook_website_url.png)
+ ![Facebook Website URL](img/facebook_website_url.png)
1. Choose "Next"
@@ -29,7 +29,7 @@ To enable the Facebook OmniAuth provider you must register your application with
1. Fill in a contact email for your app
- ![Facebook App Settings](img/facebook_app_settings.png)
+ ![Facebook App Settings](img/facebook_app_settings.png)
1. Choose "Save Changes"
@@ -45,7 +45,7 @@ To enable the Facebook OmniAuth provider you must register your application with
1. You should now see an app key and app secret (see screenshot). Keep this page open as you continue configuration.
- ![Facebook API Keys](img/facebook_api_keys.png)
+ ![Facebook API Keys](img/facebook_api_keys.png)
1. On your GitLab server, open the configuration file.
diff --git a/doc/integration/jira_development_panel.md b/doc/integration/jira_development_panel.md
index 5e906e5af16..3e894371df9 100644
--- a/doc/integration/jira_development_panel.md
+++ b/doc/integration/jira_development_panel.md
@@ -43,67 +43,68 @@ There are no special requirements if you are using GitLab.com.
1. In GitLab, create a new application in order to allow Jira to connect with your GitLab account
- While logged-in, go to `Settings -> Applications`. (Click your profile avatar at
- the top right, choose `Settings`, and then navigate to `Applications` from the left
- navigation menu.) Use the form to create a new application.
+ While logged-in, go to `Settings -> Applications`. (Click your profile avatar at
+ the top right, choose `Settings`, and then navigate to `Applications` from the left
+ navigation menu.) Use the form to create a new application.
- Enter a useful name for the `Name` field.
+ Enter a useful name for the `Name` field.
- For the `Redirect URI` field, enter `https://<your-gitlab-instance-domain>/login/oauth/callback`,
- replacing `<your-gitlab-instance-domain>` appropriately. So for example, if you are using GitLab.com,
- this would be `https://gitlab.com/login/oauth/callback`.
+ For the `Redirect URI` field, enter `https://<your-gitlab-instance-domain>/login/oauth/callback`,
+ replacing `<your-gitlab-instance-domain>` appropriately. So for example, if you are using GitLab.com,
+ this would be `https://gitlab.com/login/oauth/callback`.
- NOTE: **Note**:
- If using a GitLab version earlier than 11.3 the `Redirect URI` value should be `https://<your-gitlab-instance-domain>/-/jira/login/oauth/callback`.
+ NOTE: **Note**:
+ If using a GitLab version earlier than 11.3 the `Redirect URI` value should be `https://<your-gitlab-instance-domain>/-/jira/login/oauth/callback`.
- ![GitLab Application setup](img/jira_dev_panel_gl_setup_1.png)
- - Check `api` in the Scopes section.
+ ![GitLab Application setup](img/jira_dev_panel_gl_setup_1.png)
+
+ - Check `api` in the Scopes section.
1. Click `Save application`. You will see the generated 'Application Id' and 'Secret' values.
- Copy these values that you will use on the Jira configuration side.
+ Copy these values that you will use on the Jira configuration side.
## Jira Configuration
1. In Jira, from the gear menu at the top right, go to `Applications`. Navigate to `DVCS accounts`
- from the left navigation menu. Click `Link GitHub account` to start creating a new integration.
- (We are pretending to be GitHub in this integration until there is further platform support from Jira.)
+ from the left navigation menu. Click `Link GitHub account` to start creating a new integration.
+ (We are pretending to be GitHub in this integration until there is further platform support from Jira.)
- ![Jira DVCS from Dashboard](img/jira_dev_panel_jira_setup_1.png)
+ ![Jira DVCS from Dashboard](img/jira_dev_panel_jira_setup_1.png)
1. Complete the form
- Select GitHub Enterprise for the `Host` field.
+ Select GitHub Enterprise for the `Host` field.
- For the `Team or User Account` field, enter the relative path of a top-level GitLab group that you have access to,
- or the relative path of your personal namespace.
+ For the `Team or User Account` field, enter the relative path of a top-level GitLab group that you have access to,
+ or the relative path of your personal namespace.
- ![Creation of Jira DVCS integration](img/jira_dev_panel_jira_setup_2.png)
+ ![Creation of Jira DVCS integration](img/jira_dev_panel_jira_setup_2.png)
- For the `Host URL` field, enter `https://<your-gitlab-instance-domain>/`,
- replacing `<your-gitlab-instance-domain>` appropriately. So for example, if you are using GitLab.com,
- this would be `https://gitlab.com/`.
+ For the `Host URL` field, enter `https://<your-gitlab-instance-domain>/`,
+ replacing `<your-gitlab-instance-domain>` appropriately. So for example, if you are using GitLab.com,
+ this would be `https://gitlab.com/`.
- NOTE: **Note**:
- If using a GitLab version earlier than 11.3 the `Host URL` value should be `https://<your-gitlab-instance-domain>/-/jira`
+ NOTE: **Note**:
+ If using a GitLab version earlier than 11.3 the `Host URL` value should be `https://<your-gitlab-instance-domain>/-/jira`
- For the `Client ID` field, use the `Application ID` value from the previous section.
+ For the `Client ID` field, use the `Application ID` value from the previous section.
- For the `Client Secret` field, use the `Secret` value from the previous section.
+ For the `Client Secret` field, use the `Secret` value from the previous section.
- Ensure that the rest of the checkboxes are checked.
+ Ensure that the rest of the checkboxes are checked.
1. Click `Add` to complete and create the integration.
- Jira takes up to a few minutes to know about (import behind the scenes) all the commits and branches
- for all the projects in the GitLab group you specified in the previous step. These are refreshed
- every 60 minutes.
+ Jira takes up to a few minutes to know about (import behind the scenes) all the commits and branches
+ for all the projects in the GitLab group you specified in the previous step. These are refreshed
+ every 60 minutes.
- > **Note:**
- > In the future, we plan on implementating real-time integration. If you need
- > to refresh the data manually, you can do this from the `Applications -> DVCS
- > accounts` screen where you initially set up the integration:
- >
- > ![Refresh GitLab information in Jira](img/jira_dev_panel_manual_refresh.png)
+ > **Note:**
+ > In the future, we plan on implementating real-time integration. If you need
+ > to refresh the data manually, you can do this from the `Applications -> DVCS
+ > accounts` screen where you initially set up the integration:
+ >
+ > ![Refresh GitLab information in Jira](img/jira_dev_panel_manual_refresh.png)
To connect additional GitLab projects from other GitLab top-level groups (or personal namespaces), repeat the above
steps with additional Jira DVCS accounts.
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index b4f2025265e..b791cbd428d 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -67,8 +67,6 @@ For source installations, make sure the `kerberos` gem group
1. [Restart GitLab] for the changes to take effect.
----
-
**Omnibus package installations**
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -83,8 +81,6 @@ For source installations, make sure the `kerberos` gem group
1. [Reconfigure GitLab] for the changes to take effect.
----
-
GitLab will now offer the `negotiate` authentication method for signing in and
HTTP Git access, enabling Git clients that support this authentication protocol
to authenticate with Kerberos tokens.
@@ -172,8 +168,6 @@ keep offering only `basic` authentication.
1. [Restart GitLab] and NGINX for the changes to take effect.
----
-
**For Omnibus package installations**
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -186,8 +180,6 @@ keep offering only `basic` authentication.
1. [Reconfigure GitLab] for the changes to take effect.
----
-
After this change, all Git remote URLs will have to be updated to
`https://gitlab.example.com:8443/mygroup/myproject.git` in order to use
Kerberos ticket-based authentication.
@@ -223,8 +215,6 @@ remove the OmniAuth provider named `kerberos` from your `gitlab.yml` /
1. [Restart GitLab] for the changes to take effect.
----
-
**For Omnibus installations**
1. Edit `/etc/gitlab/gitlab.rb` and remove the `{ "name" => "kerberos" }` line
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index fc36b961b3f..9317ba0c71e 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -2,7 +2,7 @@
type: index, reference
---
-# Customers
+# Customer Docs
This section contains information for:
diff --git a/doc/tools/email.md b/doc/tools/email.md
index 72a5d094bc9..3088b0b63e7 100644
--- a/doc/tools/email.md
+++ b/doc/tools/email.md
@@ -19,12 +19,12 @@ at their primary email address.
1. Go to the admin area using the wrench icon in the top right corner and
navigate to **Overview > Users > Send email to users**.
- ![admin users](email1.png)
+ ![admin users](email1.png)
1. Compose an email and choose where it will be sent (all users or users of a
chosen group or project):
- ![compose an email](email2.png)
+ ![compose an email](email2.png)
## Unsubscribing from emails
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index c1771a57da0..7ab59b80374 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -38,13 +38,13 @@ those projects provide a barebones application built on some well-known framewor
Rails, Spring, or NodeJS Express project. For this example,
we'll use the Ruby on Rails template.
- ![Select project template](img/guide_project_template.png)
+ ![Select project template](img/guide_project_template.png)
1. Give your project a name, optionally a description, and make it public so that
you can take advantage of the features available in the
[GitLab Gold plan](https://about.gitlab.com/pricing/#gitlab-com).
- ![Create project](img/guide_create_project.png)
+ ![Create project](img/guide_create_project.png)
1. Click **Create project**.
@@ -56,20 +56,20 @@ under which this application will be deployed.
1. On the project's landing page, click the button labeled **Add Kubernetes cluster**
(note that this option is also available when you navigate to **Operations > Kubernetes**).
- ![Project landing page](img/guide_project_landing_page.png)
+ ![Project landing page](img/guide_project_landing_page.png)
1. Choose **Create on Google Kubernetes Engine**.
- ![Choose GKE](img/guide_choose_gke.png)
+ ![Choose GKE](img/guide_choose_gke.png)
1. Sign in with Google.
- ![Google sign in](img/guide_google_signin.png)
+ ![Google sign in](img/guide_google_signin.png)
1. Connect with your Google account and press **Allow** when asked (this will
be shown only the first time you connect GitLab with your Google account).
- ![Google auth](img/guide_google_auth.png)
+ ![Google auth](img/guide_google_auth.png)
1. The last step is to fill in the cluster details. Give it a name, leave the
environment scope as is, and choose the GCP project under which the cluster
@@ -80,7 +80,7 @@ under which this application will be deployed.
cluster will be created, enter the number of nodes you want it to have, and
finally choose their [machine type](https://cloud.google.com/compute/docs/machine-types).
- ![GitLab GKE cluster details](img/guide_gitlab_gke_details.png)
+ ![GitLab GKE cluster details](img/guide_gitlab_gke_details.png)
1. Once ready, click **Create Kubernetes cluster**.
@@ -133,7 +133,7 @@ Now that the Kubernetes cluster is set up and ready, let's enable Auto DevOps.
successfully runs on the `master` branch.
1. Click **Save changes**.
- ![Auto DevOps settings](img/guide_enable_autodevops.png)
+ ![Auto DevOps settings](img/guide_enable_autodevops.png)
Once you complete all the above and save your changes, a new pipeline is
automatically created. To view the pipeline, go to **CI/CD > Pipelines**.
@@ -201,7 +201,7 @@ applications. In the rightmost column for the production environment, you can ma
Prometheus collects data about the Kubernetes cluster and how the application
affects it (in terms of memory/CPU usage, latency, etc.).
- ![Environments metrics](img/guide_environments_metrics.png)
+ ![Environments metrics](img/guide_environments_metrics.png)
- The third icon is the [web terminal](../../ci/environments.md#web-terminals)
and it will open a terminal session right inside the container where the
diff --git a/doc/topics/git/partial_clone.md b/doc/topics/git/partial_clone.md
index f2951308ba1..ea4223355d8 100644
--- a/doc/topics/git/partial_clone.md
+++ b/doc/topics/git/partial_clone.md
@@ -99,49 +99,49 @@ enabled on the Git server:
shared-component-b/
```
-2. *Create a new Git repository and fetch.* Support for `--filter=sparse:oid`
+1. *Create a new Git repository and fetch.* Support for `--filter=sparse:oid`
using the clone command is incomplete, so we will emulate the clone command
by hand, using `git init` and `git fetch`. Follow
[gitaly#1769](https://gitlab.com/gitlab-org/gitaly/issues/1769) for updates.
- ```bash
- # Create a new directory for the Git repository
- mkdir jumbo-repo && cd jumbo-repo
+ ```bash
+ # Create a new directory for the Git repository
+ mkdir jumbo-repo && cd jumbo-repo
- # Initialize a new Git repository
- git init
+ # Initialize a new Git repository
+ git init
- # Add the remote
- git remote add origin git@gitlab.com/example/jumbo-repo
+ # Add the remote
+ git remote add origin git@gitlab.com/example/jumbo-repo
- # Enable partial clone support for the remote
- git config --local extensions.partialClone origin
+ # Enable partial clone support for the remote
+ git config --local extensions.partialClone origin
- # Fetch the filtered set of objects using the filterspec stored on the
- # server. WARNING: this step is slow!
- git fetch --filter=sparse:oid=master:shiny-app/.gitfilterspec origin
+ # Fetch the filtered set of objects using the filterspec stored on the
+ # server. WARNING: this step is slow!
+ git fetch --filter=sparse:oid=master:shiny-app/.gitfilterspec origin
- # Optional: observe there are missing objects that we have not fetched
- git rev-list --all --quiet --objects --missing=print | wc -l
- ```
+ # Optional: observe there are missing objects that we have not fetched
+ git rev-list --all --quiet --objects --missing=print | wc -l
+ ```
- CAUTION: **IDE and Shell integrations:**
- Git integrations with `bash`, `zsh`, etc and editors that automatically
- show Git status information often run `git fetch` which will fetch the
- entire repository. You many need to disable or reconfigure these
- integrations.
+ CAUTION: **IDE and Shell integrations:**
+ Git integrations with `bash`, `zsh`, etc and editors that automatically
+ show Git status information often run `git fetch` which will fetch the
+ entire repository. You many need to disable or reconfigure these
+ integrations.
-3. **Sparse checkout** must be enabled and configured to prevent objects from
+1. **Sparse checkout** must be enabled and configured to prevent objects from
other paths being downloaded automatically when checking out branches. Follow
[gitaly#1765](https://gitlab.com/gitlab-org/gitaly/issues/1765) for updates.
- ```bash
- # Enable sparse checkout
- git config --local core.sparsecheckout true
+ ```bash
+ # Enable sparse checkout
+ git config --local core.sparsecheckout true
- # Configure sparse checkout
- git show master:snazzy-app/.gitfilterspec >> .git/info/sparse-checkout
+ # Configure sparse checkout
+ git show master:snazzy-app/.gitfilterspec >> .git/info/sparse-checkout
- # Checkout master
- git checkout master
- ```
+ # Checkout master
+ git checkout master
+ ```
diff --git a/doc/update/README.md b/doc/update/README.md
index 974982da5d0..42c43110a19 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -135,6 +135,30 @@ If you need to downgrade your Enterprise Edition installation back to Community
Edition, you can follow [this guide][ee-ce] to make the process as smooth as
possible.
+## Version specific upgrading instructions
+
+### 12.2.0
+
+In 12.2.0, we enabled Rails' authenticated cookie encryption. Old sessions are
+automatically upgraded.
+
+However, session cookie downgrades are not supported. So after upgrading to 12.2.0,
+any downgrades would result to all sessions being invalidated and users are logged out.
+
+### 12.0.0
+
+In 12.0.0 we made various database related changes. These changes require that
+users first upgrade to the latest 11.11 patch release. Once upgraded to 11.11.x,
+users can upgrade to 12.x. Failure to do so may result in database migrations
+not being applied, which could lead to application errors.
+
+Example 1: you are currently using GitLab 11.11.3, which is the latest patch
+release for 11.11.x. You can upgrade as usual to 12.0.0, 12.1.0, etc.
+
+Example 2: you are currently using a version of GitLab 10.x. To upgrade, first
+upgrade to 11.11.3. Once upgraded to 11.11.3 you can safely upgrade to 12.0.0
+or future versions.
+
## Miscellaneous
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index d3b0a3c2829..0aef40262c9 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -378,20 +378,6 @@ Example:
Additional instructions here.
-->
-### 12.0.0
-
-In 12.0.0 we made various database related changes. These changes require that
-users first upgrade to the latest 11.11 patch release. Once upgraded to 11.11.x,
-users can upgrade to 12.x. Failure to do so may result in database migrations
-not being applied, which could lead to application errors.
-
-Example 1: you are currently using GitLab 11.11.3, which is the latest patch
-release for 11.11.x. You can upgrade as usual to 12.0.0, 12.1.0, etc.
-
-Example 2: you are currently using a version of GitLab 10.x. To upgrade, first
-upgrade to 11.11.3. Once upgraded to 11.11.3 you can safely upgrade to 12.0.0
-or future versions.
-
## Things went south? Revert to previous version
### 1. Revert the code to the previous version
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index bbd04146eb2..f5864e1f828 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -30,22 +30,22 @@ Otherwise, you can:
1. Navigate manually to the **Admin Area** by clicking the wrench icon in the menu bar.
- ![Admin area icon](img/admin_wrench.png)
+ ![Admin area icon](img/admin_wrench.png)
1. And then going to the **License** tab and click on **Upload New License**.
- ![License admin area](img/license_admin_area.png)
+ ![License admin area](img/license_admin_area.png)
1. If you've received a `.gitlab-license` file, you should have already downloaded
it in your local machine. You can then upload it directly by choosing the
license file and clicking the **Upload license** button. In the image below,
you can see that the selected license file is named `GitLab.gitlab-license`.
- ![Upload license](img/license_upload.png)
+ ![Upload license](img/license_upload.png)
- If you've received your license as plain text, you need to select the
- "Enter license key" option, copy the license, paste it into the "License key"
- field and click **Upload license**.
+ If you've received your license as plain text, you need to select the
+ "Enter license key" option, copy the license, paste it into the "License key"
+ field and click **Upload license**.
## Add your license at install time
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index e05b3395535..43640f1b16a 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -107,18 +107,18 @@ In order to purchase additional minutes, you should follow these steps:
1. Go to **Group > Settings > Pipelines quota**. Once you are on that page, click on **Buy additional minutes**.
- ![Buy additional minutes](img/buy_btn.png)
+ ![Buy additional minutes](img/buy_btn.png)
1. Locate the subscription card that is linked to your group on GitLab.com,
click on **Buy more CI minutes**, and complete the details about the transaction.
- ![Buy additional minutes](img/buy_minutes_card.png)
+ ![Buy additional minutes](img/buy_minutes_card.png)
1. Once we have processed your payment, the extra CI minutes
will be synced to your Group and you can visualize it from the
**Group > Settings > Pipelines quota** page:
- ![Additional minutes](img/additional_minutes.png)
+ ![Additional minutes](img/additional_minutes.png)
Be aware that:
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 5149f628345..2f15d997b5b 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -164,7 +164,7 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. |
| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
-| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 1=Medium, 3=High. |
+| `SAST_GOSEC_LEVEL` | 0 | Ignore gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. |
| `SAST_EXCLUDED_PATHS` | - | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, file or folder paths. Parent directories will also match patterns. |
### Timeouts
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 3cb765c0463..6891682141c 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -22,7 +22,7 @@ higher can also edit a comment made by someone else.
You can also reply to a comment notification email to reply to the comment if
[Reply by email] is configured for your GitLab instance. Replying to a standard comment
creates another standard comment. Replying to a threaded comment creates a reply in the thread. Email replies support
- [Markdown] and [quick actions], just as if you replied from the web.
+[Markdown] and [quick actions], just as if you replied from the web.
## Resolvable comments and threads
@@ -58,17 +58,17 @@ To create a commit diff thread:
1. Navigate to the merge request **Commits** tab. A list of commits that
constitute the merge request will be shown.
- ![Merge request commits tab](img/merge_request_commits_tab.png)
+ ![Merge request commits tab](img/merge_request_commits_tab.png)
1. Navigate to a specific commit, click on the **Changes** tab (where you
will only be presented diffs from the selected commit), and leave a comment.
- ![Commit diff discussion in merge request context](img/commit_comment_mr_context.png)
+ ![Commit diff discussion in merge request context](img/commit_comment_mr_context.png)
1. Any threads created this way will be shown in the merge request's
**Discussions** tab and are resolvable.
- ![Merge request Discussions tab](img/commit_comment_mr_discussions_tab.png)
+ ![Merge request Discussions tab](img/commit_comment_mr_discussions_tab.png)
Threads created this way will only appear in the original merge request
and not when navigating to that commit under your project's
@@ -88,6 +88,11 @@ Jump button next to the Reply field on a thread.
You can also jump to the first unresolved thread from the button next to the
resolved threads tracker.
+You can also use keyboard shortcuts to navigate among threads:
+
+- Use <kbd>n</kbd> to jump to the next unresolved thread.
+- Use <kbd>p</kbd> to jump to the previous unresolved thread.
+
!["8/9 threads resolved"](img/threads_resolved.png)
### Marking a comment or thread as resolved
@@ -338,8 +343,8 @@ bottom of the screen with two buttons:
- **Discard**: Discards all comments that have not been submitted.
- **Finish review**: Opens a list of comments ready to be submitted for review.
- Clicking **Submit review** will publish all comments. Any quick actions
- submitted are performed at this time.
+ Clicking **Submit review** will publish all comments. Any quick actions
+ submitted are performed at this time.
Alternatively, every pending comment has a button to finish the entire review.
@@ -384,18 +389,18 @@ the Merge Request authored by the user that applied them.
1. Choose a line of code to be changed, add a new comment, then click
on the **Insert suggestion** icon in the toolbar:
- ![Add a new comment](img/insert_suggestion.png)
+ ![Add a new comment](img/insert_suggestion.png)
1. In the comment, add your suggestion to the pre-populated code block:
- ![Add a suggestion into a code block tagged properly](img/make_suggestion.png)
+ ![Add a suggestion into a code block tagged properly](img/make_suggestion.png)
1. Click **Comment**.
- The suggestions in the comment can be applied by the merge request author
- directly from the merge request:
+ The suggestions in the comment can be applied by the merge request author
+ directly from the merge request:
- ![Apply suggestions](img/suggestion.png)
+ ![Apply suggestions](img/suggestion.png)
Once the author applies a suggestion, it will be marked with the **Applied** label,
the thread will be automatically resolved, and GitLab will create a new commit
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index 2d37fc375db..a0dd5cd497d 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -4,7 +4,8 @@ type: reference
# Contribution Analytics **(STARTER)**
-> Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
+> - Introduced in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/3090) for subgroups in GitLab 12.2.
## Overview
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 98e1c654d0e..43fd0bfd45a 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -91,11 +91,11 @@ To create a new Group, either:
- In the top menu, click **Groups** and then **Your Groups**, and click the green button **New group**.
- ![new group from groups page](img/new_group_from_groups.png)
+ ![new group from groups page](img/new_group_from_groups.png)
- Or, in the top menu, expand the `plus` sign and choose **New group**.
- ![new group from elsewhere](img/new_group_from_other_pages.png)
+ ![new group from elsewhere](img/new_group_from_other_pages.png)
Add the following information:
@@ -104,15 +104,15 @@ Add the following information:
1. The **Group name** will automatically populate the URL. Optionally, you can change it.
This is the name that displays in group views.
The name can contain only:
- - Alphanumeric characters
- - Underscores
- - Dashes and dots
- - Spaces
+ - Alphanumeric characters
+ - Underscores
+ - Dashes and dots
+ - Spaces
1. The **Group URL** is the namespace under which your projects will be hosted.
The URL can contain only:
- - Alphanumeric characters
- - Underscores
- - Dashes and dots (it cannot start with dashes or end in a dot)
+ - Alphanumeric characters
+ - Underscores
+ - Dashes and dots (it cannot start with dashes or end in a dot)
1. Optionally, you can add a brief description to tell others
what this group is about.
1. Optionally, choose an avatar for your group.
@@ -166,12 +166,12 @@ There are two different ways to add a new project to a group:
- Select a group, and then click **New project**. You can then continue [creating your project](../../gitlab-basics/create-project.md).
- ![New project](img/create_new_project_from_group.png)
+ ![New project](img/create_new_project_from_group.png)
- While you are creating a project, select a group namespace
you've already created from the dropdown menu.
- ![Select group](img/select_group_dropdown.png)
+ ![Select group](img/select_group_dropdown.png)
### Default project-creation level
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 64ac4f15d5f..f8bef8b8a6a 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -90,11 +90,11 @@ You can then test the connection by clicking on **Test Connection**. If the conn
1. Create a new mapping by clicking **Add New Mapping** then set **Source attribute** to `objectId`, and **Target attribute** to `externalId`.
1. Click the `userPrincipalName` mapping and change **Match objects using this attribute** to `No`.
- Save your changes and you should have the following configuration:
+ Save your changes and you should have the following configuration:
![Azure's attribute mapping configuration](img/scim_attribute_mapping.png)
- NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`.
+ NOTE: **Note:** If you used a unique identifier **other than** `objectId`, be sure to map it instead to both `id` and `externalId`.
1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**.
1. Leave the `id` as the primary and only required field.
@@ -108,7 +108,7 @@ You can then test the connection by clicking on **Test Connection**. If the conn
1. Save all the screens and, in the **Provisioning** step, set
the `Provisioning Status` to `On`.
- ![Provisioning status toggle switch](img/scim_provisioning_status.png)
+ ![Provisioning status toggle switch](img/scim_provisioning_status.png)
NOTE: **Note:**
You can control what is actually synced by selecting the `Scope`. For example,
diff --git a/doc/user/index.md b/doc/user/index.md
index db2759727a5..c93f64cd528 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -2,7 +2,7 @@
description: 'Read through the GitLab User documentation to learn how to use, configure, and customize GitLab and GitLab.com to your own needs.'
---
-# User documentation
+# User Docs
Welcome to GitLab! We're glad to have you here!
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index dec679fc975..dce8c3a97bc 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -40,7 +40,7 @@ To lock a file:
1. Pick the file you want to lock.
1. Click the "Lock" button.
- ![Locking file](img/file_lock.png)
+ ![Locking file](img/file_lock.png)
To lock an entire directory, look for the "Lock" link next to "History".
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index 1cc3bd3363d..e509e333313 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -31,12 +31,12 @@ to enable this if not already.
## How it works
When issues/pull requests are being imported, the Bitbucket importer tries to find
-the Bitbucket author/assignee in GitLab's database using the Bitbucket ID. For this
-to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
-and **associated their Bitbucket account**. If the user is not
-found in GitLab's database, the project creator (most of the times the current
-user that started the import process) is set as the author, but a reference on
-the issue about the original Bitbucket author is kept.
+the Bitbucket author/assignee in GitLab's database using the Bitbucket `nickname`.
+For this to work, the Bitbucket author/assignee should have signed in beforehand in GitLab
+and **associated their Bitbucket account**. Their `nickname` must also match their Bitbucket
+`username.`. If the user is not found in GitLab's database, the project creator
+(most of the times the current user that started the import process) is set as the author,
+but a reference on the issue about the original Bitbucket author is kept.
The importer will create any new namespaces (groups) if they don't exist or in
the case the namespace is taken, the repository will be imported under the user's
@@ -49,17 +49,17 @@ namespace that started the import process.
1. Click on the "Bitbucket Cloud" button.
- ![Bitbucket](img/import_projects_from_new_project_page.png)
+ ![Bitbucket](img/import_projects_from_new_project_page.png)
1. Grant GitLab access to your Bitbucket account
- ![Grant access](img/bitbucket_import_grant_access.png)
+ ![Grant access](img/bitbucket_import_grant_access.png)
1. Click on the projects that you'd like to import or **Import all projects**.
You can also select the namespace under which each project will be
imported.
- ![Import projects](img/bitbucket_import_select_project.png)
+ ![Import projects](img/bitbucket_import_select_project.png)
[bb-import]: ../../../integration/bitbucket.md
[social sign-in]: ../../profile/account/social_sign_in.md
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
index e4eb1a9a392..28e211ee2ba 100644
--- a/doc/user/project/import/bitbucket_server.md
+++ b/doc/user/project/import/bitbucket_server.md
@@ -32,6 +32,8 @@ Import your projects from Bitbucket Server to GitLab with minimal effort.
1. Attachments in Markdown are currently not imported.
1. Task lists are not imported.
1. Emoji reactions are not imported
+1. Project filtering does not support fuzzy search (only `starts with` or `full
+ match strings` are currently supported)
## How it works
@@ -59,16 +61,16 @@ namespace that started the import process.
1. Sign in to GitLab and go to your dashboard.
1. Click on **New project**.
1. Click on the "Bitbucket Server" button. If the button is not present, enable the importer in
- **Admin > Application Settings > Visibility and access controls > Import sources**.
+ **Admin > Application Settings > Visibility and access controls > Import sources**.
- ![Bitbucket](img/import_projects_from_new_project_page.png)
+ ![Bitbucket](img/import_projects_from_new_project_page.png)
1. Enter your Bitbucket Server credentials.
- ![Grant access](img/bitbucket_server_import_credentials.png)
+ ![Grant access](img/bitbucket_server_import_credentials.png)
1. Click on the projects that you'd like to import or **Import all projects**.
- You can also select the namespace under which each project will be
+ You can also filter projects by name and select the namespace under which each project will be
imported.
- ![Import projects](img/bitbucket_server_import_select_project.png)
+ ![Import projects](img/bitbucket_server_import_select_project_v12_3.png)
diff --git a/doc/user/project/import/gemnasium.md b/doc/user/project/import/gemnasium.md
index 0afa32e4133..cf48189fa6e 100644
--- a/doc/user/project/import/gemnasium.md
+++ b/doc/user/project/import/gemnasium.md
@@ -5,13 +5,17 @@ instance or GitLab.com.
## Why is Gemnasium.com closed?
-Gemnasium has been [acquired by GitLab](https://about.gitlab.com/press/releases/2018-01-30-gemnasium-acquisition.html)
+Gemnasium was [acquired by GitLab](https://about.gitlab.com/press/releases/2018-01-30-gemnasium-acquisition.html)
in January 2018. Since May 15, 2018, the services provided by Gemnasium are no longer available.
The team behind Gemnasium has joined GitLab as the new Security Products team
-and is working on a wider range of tools than just Dependency Scanning:
-[SAST](../../application_security/sast/index.md),
-[DAST](../../application_security/dast/index.md),
-[Container Scanning](../../application_security/container_scanning/index.md) and more.
+and is working on a [wide range of tools](../../application_security/index.md),
+including:
+
+- [Dependency Scanning](../../application_security/dependency_scanning/index.md)
+- [SAST](../../application_security/sast/index.md)
+- [DAST](../../application_security/dast/index.md)
+- [Container Scanning](../../application_security/container_scanning/index.md)
+
If you want to continue monitoring your dependencies, see the
[Migrating to GitLab](#migrating-to-gitlab) section below.
@@ -57,27 +61,27 @@ back to both GitLab and GitHub when completed.
1. Create a new project, and select the "CI/CD for external repo" tab:
- ![Create new Project](img/gemnasium/create_project.png)
+ ![Create new Project](img/gemnasium/create_project.png)
1. Use the "GitHub" button to connect your repositories.
- ![Connect from GitHub](img/gemnasium/connect_github.png)
+ ![Connect from GitHub](img/gemnasium/connect_github.png)
1. Select the project(s) to be set up with GitLab CI/CD and chose "Connect".
- ![Select projects](img/gemnasium/select_project.png)
+ ![Select projects](img/gemnasium/select_project.png)
- Once the configuration is done, you may click on your new
- project on GitLab.
+ Once the configuration is done, you may click on your new
+ project on GitLab.
- ![click on connected project](img/gemnasium/project_connected.png)
+ ![click on connected project](img/gemnasium/project_connected.png)
- Your project is now mirrored on GitLab, where the Runners will be able to access
- your source code and run your tests.
+ Your project is now mirrored on GitLab, where the Runners will be able to access
+ your source code and run your tests.
- Optional step: If you set this up on GitLab.com, make sure the project is
- public (in the project settings) if your GitHub project is public, since
- the security feature is available only for [GitLab Ultimate](https://about.gitlab.com/pricing).
+ Optional step: If you set this up on GitLab.com, make sure the project is
+ public (in the project settings) if your GitHub project is public, since
+ the security feature is available only for [GitLab Ultimate](https://about.gitlab.com/pricing).
1. To set up the dependency scanning job, corresponding to what Gemnasium was
doing, you must create a `.gitlab-ci.yml` file, or update it according to
@@ -85,16 +89,16 @@ back to both GitLab and GitHub when completed.
The mirroring is pull-only by default, so you may create or update the file on
GitHub:
- ![Edit gitlab-ci.yml file](img/gemnasium/edit_gitlab-ci.png)
+ ![Edit gitlab-ci.yml file](img/gemnasium/edit_gitlab-ci.png)
1. Once your file has been committed, a new pipeline will be automatically
triggered if your file is valid:
- ![pipeline](img/gemnasium/pipeline.png)
+ ![pipeline](img/gemnasium/pipeline.png)
1. The result of the job will be visible directly from the pipeline view:
- ![security report](img/gemnasium/report.png)
+ ![security report](img/gemnasium/report.png)
NOTE: **Note:**
If you don't commit very often to your project, you may want to use
diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project.png b/doc/user/project/import/img/bitbucket_server_import_select_project.png
deleted file mode 100644
index e7fddef9955..00000000000
--- a/doc/user/project/import/img/bitbucket_server_import_select_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/bitbucket_server_import_select_project_v12_3.png b/doc/user/project/import/img/bitbucket_server_import_select_project_v12_3.png
new file mode 100644
index 00000000000..1c344853cc8
--- /dev/null
+++ b/doc/user/project/import/img/bitbucket_server_import_select_project_v12_3.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 9d7463416d8..ecd491d8e5b 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -10,7 +10,7 @@
1. [From Gitea](gitea.md)
1. [From Perforce](perforce.md)
1. [From SVN](svn.md)
-1. [From TFS](tfs.md)
+1. [From TFVC](tfvc.md)
1. [From repo by URL](repo_by_url.md)
1. [By uploading a manifest file (AOSP)](manifest.md)
1. [From Gemnasium](gemnasium.md)
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
index baf410d9c9e..161a4163e42 100644
--- a/doc/user/project/import/manifest.md
+++ b/doc/user/project/import/manifest.md
@@ -1,7 +1,6 @@
# Import multiple repositories by uploading a manifest file
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28811) in
-GitLab 11.2.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28811) in GitLab 11.2.
GitLab allows you to import all the required Git repositories
based on a manifest file like the one used by the
@@ -56,4 +55,4 @@ You can start the import with:
to the import status page with projects list based on the manifest file.
1. Check the list and click **Import all repositories** to start the import.
- ![Manifest status](img/manifest_status.png)
+ ![Manifest status](img/manifest_status.png)
diff --git a/doc/user/project/import/tfs.md b/doc/user/project/import/tfs.md
index 01bbb7e6ffd..7b3b11b9519 100644
--- a/doc/user/project/import/tfs.md
+++ b/doc/user/project/import/tfs.md
@@ -1,42 +1,5 @@
-# Migrating from TFS
+---
+redirect_to: 'tfvc.md'
+---
-[TFS](https://azure.microsoft.com/en-us/services/devops/server/) 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.
-
-## TFVC vs Git
-
-The following list illustrates the main differences between TFVC and Git:
-
-- **Git is distributed** whereas TFVC is centralized using a client-server
- architecture. This translates to Git having a more flexible workflow since
- your working area is a copy of the entire repository. This decreases the
- overhead when switching branches or merging for example, since you don't have
- to communicate with a remote server.
-- **Storage method.** Changes in CVS are per file (changeset), while in 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://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
-
-Migrating to Git/GitLab there is:
-
-- **No licensing costs**, Git is GPL while TFVC is proprietary.
-- **Shorter learning curve**, Git has a big community and a vast number of
- tutorials to get you started (see our [Git topic](../../../topics/git/index.md)).
-- **Integration with modern tools**, migrating to Git and GitLab you can have
- an open source end-to-end software development platform with built-in version
- control, issue tracking, code review, CI/CD, and more.
-
-## How to migrate
-
-The best option to migrate from TFVC to Git is to use the
-[`git-tfs`](https://github.com/git-tfs/git-tfs) tool. A specific guide for the
-migration exists:
-[Migrate TFS to Git](https://github.com/git-tfs/git-tfs/blob/master/doc/usecases/migrate_tfs_to_git.md).
+This document was moved to [another location](tfvc.md).
diff --git a/doc/user/project/import/tfvc.md b/doc/user/project/import/tfvc.md
new file mode 100644
index 00000000000..375522b77d0
--- /dev/null
+++ b/doc/user/project/import/tfvc.md
@@ -0,0 +1,46 @@
+---
+type: concepts
+---
+
+# Migrating from TFVC to Git
+
+Team Foundation Server (TFS), renamed [Azure DevOps Server](https://azure.microsoft.com/en-us/services/devops/server/)
+in 2019, is a set of tools developed by Microsoft which also includes
+[Team Foundation Version Control](https://docs.microsoft.com/en-us/azure/devops/repos/tfvc/overview)
+(TFVC), a centralized version control system similar to Git.
+
+In this document, we focus on the TFVC to Git migration.
+
+## TFVC vs Git
+
+The main differences between TFVC and Git are:
+
+- **Git is distributed:** While TFVC is centralized using a client-server architecture,
+ Git is distributed. This translates to Git having a more flexible workflow since
+ you work with a copy of the entire repository. This allows you to quickly
+ switch branches or merge, for example, without needing to communicate with a remote server.
+- **Storage:** Changes in a centralized version control system are per file (changeset),
+ while in Git a committed file is stored in its entirety (snapshot). That means that it is
+ very easy to revert or undo a whole change in Git.
+
+For more information, see:
+
+- Microsoft's [comparison of Git and TFVC](https://docs.microsoft.com/en-us/azure/devops/repos/tfvc/comparison-git-tfvc?view=azure-devops).
+- The Wikipedia [comparison of version control software](https://en.wikipedia.org/wiki/Comparison_of_version_control_software).
+
+## Why migrate
+
+Advantages of migrating to Git/GitLab:
+
+- **No licensing costs:** Git is open source, while TFVC is proprietary.
+- **Shorter learning curve:** Git has a big community and a vast number of
+ tutorials to get you started (see our [Git topic](../../../topics/git/index.md)).
+- **Integration with modern tools:** After migrating to Git and GitLab, you will have
+ an open source, end-to-end software development platform with built-in version
+ control, issue tracking, code review, CI/CD, and more.
+
+## How to migrate
+
+The best option to migrate from TFVC to Git is to use the [`git-tfs`](https://github.com/git-tfs/git-tfs)
+tool. Read the [Migrate TFS to Git](https://github.com/git-tfs/git-tfs/blob/master/doc/usecases/migrate_tfs_to_git.md)
+guide for more details.
diff --git a/doc/user/project/integrations/jira_cloud_configuration.md b/doc/user/project/integrations/jira_cloud_configuration.md
index 5a5ba2dd168..1d5a4a3d4c7 100644
--- a/doc/user/project/integrations/jira_cloud_configuration.md
+++ b/doc/user/project/integrations/jira_cloud_configuration.md
@@ -9,7 +9,7 @@ below to create one:
It is important that the user associated with this email address has *write* access
to projects in Jira.
-2. Click **Create API token**.
+1. Click **Create API token**.
![Jira API token](img/jira_api_token_menu.png)
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 41be26c1d30..a3a2568445e 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -40,17 +40,13 @@ the administrator console.
1. Log in with an account that has admin privileges and navigate to the system
console.
- ![Mattermost go to console](img/mattermost_goto_console.png)
-
- ---
+ ![Mattermost go to console](img/mattermost_goto_console.png)
1. Click **Custom integrations** and set **Enable Custom Slash Commands**,
**Enable custom integrations to override usernames**, and **Override
custom integrations to override profile picture icons** to true
- ![Mattermost console](img/mattermost_console_integrations.png)
-
- ---
+ ![Mattermost console](img/mattermost_console_integrations.png)
1. Click **Save** at the bottom to save the changes.
@@ -62,14 +58,12 @@ the administrator console.
A screen will appear with all the values you need to copy in Mattermost as
described in the next step. Leave the window open.
- >**Note:**
- GitLab will propose some values for the Mattermost settings. The only one
- required to copy-paste as-is is the **Request URL**, all the others are just
- suggestions.
-
- ![Mattermost setup instructions](img/mattermost_config_help.png)
+ NOTE: **Note:**
+ GitLab will propose some values for the Mattermost settings. The only one
+ required to copy-paste as-is is the **Request URL**, all the others are just
+ suggestions.
- ---
+ ![Mattermost setup instructions](img/mattermost_config_help.png)
1. Proceed to the next step and create a slash command in Mattermost with the
above values.
@@ -83,44 +77,38 @@ in a new slash command.
1. Back to Mattermost, under your team page settings, you should see the
**Integrations** option.
- ![Mattermost team integrations](img/mattermost_team_integrations.png)
-
- ---
+ ![Mattermost team integrations](img/mattermost_team_integrations.png)
1. Go to the **Slash Commands** integration and add a new one by clicking the
**Add Slash Command** button.
- ![Mattermost add command](img/mattermost_add_slash_command.png)
-
- ---
+ ![Mattermost add command](img/mattermost_add_slash_command.png)
1. Fill in the options for the custom command as described in
[step 2](#step-2-open-the-mattermost-slash-commands-service-in-gitlab).
- >**Note:**
- If you plan on connecting multiple projects, pick a slash command trigger
- word that relates to your projects such as `/gitlab-project-name` or even
- just `/project-name`. Only use `/gitlab` if you will only connect a single
- project to your Mattermost team.
+ NOTE: **Note:**
+ If you plan on connecting multiple projects, pick a slash command trigger
+ word that relates to your projects such as `/gitlab-project-name` or even
+ just `/project-name`. Only use `/gitlab` if you will only connect a single
+ project to your Mattermost team.
- ![Mattermost add command configuration](img/mattermost_slash_command_configuration.png)
+ ![Mattermost add command configuration](img/mattermost_slash_command_configuration.png)
1. After you set up all the values, copy the token (we will use it below) and
click **Done**.
- ![Mattermost slash command token](img/mattermost_slash_command_token.png)
+ ![Mattermost slash command token](img/mattermost_slash_command_token.png)
### Step 4. Copy the Mattermost token into the Mattermost slash command service
1. In GitLab, paste the Mattermost token you copied in the previous step and
check the **Active** checkbox.
- ![Mattermost copy token to GitLab](img/mattermost_gitlab_token.png)
+ ![Mattermost copy token to GitLab](img/mattermost_gitlab_token.png)
1. Click **Save changes** for the changes to take effect.
----
-
You are now set to start using slash commands in Mattermost that talk to the
GitLab project you configured.
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index bac7eecfce4..25b000b2753 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -5,18 +5,18 @@
the **Redmine** service, and fill in the required details on the page as described
in the table below.
- | Field | Description |
- | ----- | ----------- |
- | `description` | A name for the issue tracker (to differentiate between instances, for example) |
- | `project_url` | The URL to the project in Redmine which is being linked to this GitLab project |
- | `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
- | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
+ | Field | Description |
+ | ----- | ----------- |
+ | `description` | A name for the issue tracker (to differentiate between instances, for example) |
+ | `project_url` | The URL to the project in Redmine which is being linked to this GitLab project |
+ | `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
+ | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
- Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
+ Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
- As an example, below is a configuration for a project named gitlab-ci.
+ As an example, below is a configuration for a project named gitlab-ci.
- ![Redmine configuration](img/redmine_configuration.png)
+ ![Redmine configuration](img/redmine_configuration.png)
1. To disable the internal issue tracking system in a project, navigate to the General page, expand the [permissions](../settings/index.md#sharing-and-permissions) section and switch the **Issues** toggle to disabled.
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 611ff0e6bfb..9340d239677 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -20,18 +20,18 @@ To share 'Project Acme' with the 'Engineering' group:
1. For 'Project Acme' use the left navigation menu to go to **Settings > Members**
- ![share project with groups](img/share_project_with_groups.png)
+ ![share project with groups](img/share_project_with_groups.png)
1. Select the 'Share with group' tab
1. Add the 'Engineering' group with the maximum access level of your choice
1. Click **Share** to share it
- ![share project with groups tab](img/share_project_with_groups_tab.png)
+ ![share project with groups tab](img/share_project_with_groups_tab.png)
1. After sharing 'Project Acme' with 'Engineering', the project will be listed
on the group dashboard
- !['Project Acme' is listed as a shared project for 'Engineering'](img/other_group_sees_shared_project.png)
+ !['Project Acme' is listed as a shared project for 'Engineering'](img/other_group_sees_shared_project.png)
Note that you can only share a project with:
diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md
index eb6e454062a..3a409bab19d 100644
--- a/doc/user/project/merge_requests/code_quality.md
+++ b/doc/user/project/merge_requests/code_quality.md
@@ -1,5 +1,6 @@
---
type: reference, howto
+disqus_identifier: 'https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html'
---
# Code Quality **(STARTER)**
diff --git a/doc/user/project/merge_requests/code_quality_diff.md b/doc/user/project/merge_requests/code_quality_diff.md
index ccc694672a6..0aa108a2e73 100644
--- a/doc/user/project/merge_requests/code_quality_diff.md
+++ b/doc/user/project/merge_requests/code_quality_diff.md
@@ -1,5 +1,4 @@
---
-redirect_from: 'code_quality_diff.md'
redirect_to: 'code_quality.md'
---
diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md
index 656459b3b03..509576c8d6f 100644
--- a/doc/user/project/merge_requests/merge_request_approvals.md
+++ b/doc/user/project/merge_requests/merge_request_approvals.md
@@ -44,7 +44,7 @@ To edit the merge request approvals:
1. Navigate to your project's **Settings > General** and expand
**Merge request approvals**.
- ![Approvals starter project empty](img/approvals_starter_project_empty.png)
+ ![Approvals starter project empty](img/approvals_starter_project_empty.png)
1. Click **Edit**.
1. Search for users or groups that will be [eligible to approve](#eligible-approvers)
@@ -54,7 +54,7 @@ To edit the merge request approvals:
box. Note: the minimum can be 0.
1. Click **Update approvers**.
- ![Approvals starter project edit](img/approvals_starter_project_edit.png)
+ ![Approvals starter project edit](img/approvals_starter_project_edit.png)
The steps above are the minimum required to get approvals working in your
merge requests, but there are a couple more options available that might be
@@ -83,7 +83,7 @@ request approval rules:
1. Give the approval rule a name that describes the set of approvers selected.
1. Click **Add approvers** to submit the new rule.
- ![Approvals premium project edit](img/approvals_premium_project_edit.png)
+ ![Approvals premium project edit](img/approvals_premium_project_edit.png)
## Multiple approval rules **(PREMIUM)**
@@ -144,14 +144,17 @@ the following is possible:
- If the required number of approvals has _not_ been yet met, they can approve
it by clicking the displayed **Approve** button.
- ![Approve](img/approve.png)
+
+ ![Approve](img/approve.png)
+
- If the required number of approvals has already been met, they can still
approve it by clicking the displayed **Approve additionally** button.
- ![Add approval](img/approve_additionally.png)
+
+ ![Add approval](img/approve_additionally.png)
- **They have already approved this merge request**: They can remove their approval.
- ![Remove approval](img/remove_approval.png)
+ ![Remove approval](img/remove_approval.png)
NOTE: **Note:**
The merge request author is only allowed to approve their own merge request
@@ -159,7 +162,7 @@ if [**Prevent author approval**](#allowing-merge-request-authors-to-approve-thei
For a given merge request, if the approval restrictions have been satisfied,
the merge request is unblocked and can be merged.
-Note, that meeting the required number of approvals is a necessary, but not
+Note that meeting the required number of approvals is a necessary, but not
sufficient condition for unblocking a merge request from being merged. There
are other conditions that may block it, such as merge conflicts,
[pending discussions](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-threads-are-resolved)
@@ -203,7 +206,7 @@ First, you have to enable this option in the project's settings:
1. Tick the "Can override approvers and approvals required per merge request"
checkbox
- ![Approvals can override](img/approvals_can_override.png)
+ ![Approvals can override](img/approvals_can_override.png)
1. Click **Save changes**
@@ -259,7 +262,7 @@ new commits are pushed to the source branch of the merge request:
1. Tick the "Remove all approvals in a merge request when new commits are pushed to its source branch"
checkbox
- ![Approvals remove on push](img/approvals_remove_on_push.png)
+ ![Approvals remove on push](img/approvals_remove_on_push.png)
1. Click **Save changes**
diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md
index 45cb56dfb6b..e046b3466c4 100644
--- a/doc/user/project/merge_requests/merge_request_dependencies.md
+++ b/doc/user/project/merge_requests/merge_request_dependencies.md
@@ -23,11 +23,11 @@ merge requests in the same project cannot depend on each other.
## Use cases
-* Ensure changes to a library are merged before changes to a project that
+- Ensure changes to a library are merged before changes to a project that
imports the library
-* Prevent a documentation-only merge request from being merged before the merge request
+- Prevent a documentation-only merge request from being merged before the merge request
implementing the feature to be documented
-* Require an merge request updating a permissions matrix to be merged before merging an
+- Require an merge request updating a permissions matrix to be merged before merging an
merge request from someone who hasn't yet been granted permissions
It is common for a single logical change to span several merge requests, spread
@@ -97,9 +97,9 @@ merge.
## Limitations
-* API support: [gitlab-ee#12551](https://gitlab.com/gitlab-org/gitlab-ee/issues/12551)
-* Dependencies are not preserved across project export/import: [gitlab-ee#12549](https://gitlab.com/gitlab-org/gitlab-ee/issues/12549)
-* Complex merge order dependencies are not supported: [gitlab-ee#11393](https://gitlab.com/gitlab-org/gitlab-ee/issues/11393)
+- API support: [gitlab-ee#12551](https://gitlab.com/gitlab-org/gitlab-ee/issues/12551)
+- Dependencies are not preserved across project export/import: [gitlab-ee#12549](https://gitlab.com/gitlab-org/gitlab-ee/issues/12549)
+- Complex merge order dependencies are not supported: [gitlab-ee#11393](https://gitlab.com/gitlab-org/gitlab-ee/issues/11393)
The last item merits a little more explanation. Dependencies between merge
requests can be described as a graph of relationships. The simplest possible
@@ -122,7 +122,6 @@ graph LR;
Several different merge requests can also directly depend upon the
same merge request:
-
```mermaid
graph LR;
herfriend/another-lib!1-->myfriend/awesome-lib!10;
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
index 6a9900d48f9..1821d954af3 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
@@ -1,7 +1,7 @@
---
last_updated: 2019-07-04
type: reference, howto
-redirect_from: 'https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html'
+disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html'
---
# Custom domains and SSL/TLS Certificates
@@ -192,13 +192,13 @@ can use the following setup:
1. In GitLab, verify your domain.
1. In Cloudflare, create a DNS `CNAME` record pointing `www` to `domain.com`.
1. In Cloudflare, add a Page Rule pointing `www.domain.com` to `domain.com`:
- - Navigate to your domain's dashboard and click **Page Rules**
- on the top nav.
- - Click **Create Page Rule**.
- - Enter the domain `www.domain.com` and click **+ Add a Setting**.
- - From the dropdown menu, choose **Forwarding URL**, then select the
- status code **301 - Permanent Redirect**.
- - Enter the destination URL `https://domain.com`.
+ - Navigate to your domain's dashboard and click **Page Rules**
+ on the top nav.
+ - Click **Create Page Rule**.
+ - Enter the domain `www.domain.com` and click **+ Add a Setting**.
+ - From the dropdown menu, choose **Forwarding URL**, then select the
+ status code **301 - Permanent Redirect**.
+ - Enter the destination URL `https://domain.com`.
## Adding an SSL/TLS certificate to Pages
@@ -263,7 +263,6 @@ To enable this setting:
1. Navigate to your project's **Settings > Pages**.
1. Tick the checkbox **Force HTTPS (requires valid certificates)**.
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
index 4588375738e..255d535645d 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
@@ -47,7 +47,7 @@ Once you've met the requirements, to enable Let's Encrypt integration:
1. Click **Edit** in the top-right corner.
1. Enable Let's Encrypt integration by switching **Automatic certificate management using Let's Encrypt**:
- ![Enable Let's Encrypt](img/lets_encrypt_integration_v12_1.png)
+ ![Enable Let's Encrypt](img/lets_encrypt_integration_v12_1.png)
1. Click **Save changes**.
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
index 743c360f075..3bab652ca39 100644
--- a/doc/user/project/pages/getting_started_part_two.md
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -86,17 +86,17 @@ You can also take some **optional** further steps:
- _Remove the fork relationship._ The fork relationship is necessary to contribute back to the project you originally forked from. If you don't have any intentions to do so, you can remove it. To do so, navigate to your project's **Settings**, expand **Advanced settings**, and scroll down to **Remove fork relationship**:
- ![remove fork relationship](img/remove_fork_relationship.png)
+ ![remove fork relationship](img/remove_fork_relationship.png)
- _Make it a user or group website._ To turn a **project website** forked
from the Pages group into a **user/group** website, you'll need to:
- - Rename it to `namespace.gitlab.io`: go to your project's
- **Settings > General** and expand **Advanced**. Scroll down to
- **Rename repository** and change the path to `namespace.gitlab.io`.
- - Adjust your SSG's [base URL](#urls-and-baseurls) from `"project-name"` to
- `""`. This setting will be at a different place for each SSG, as each of them
- have their own structure and file tree. Most likely, it will be in the SSG's
- config file.
+ - Rename it to `namespace.gitlab.io`: go to your project's
+ **Settings > General** and expand **Advanced**. Scroll down to
+ **Rename repository** and change the path to `namespace.gitlab.io`.
+ - Adjust your SSG's [base URL](#urls-and-baseurls) from `"project-name"` to
+ `""`. This setting will be at a different place for each SSG, as each of them
+ have their own structure and file tree. Most likely, it will be in the SSG's
+ config file.
### Create a project from scratch
@@ -107,12 +107,12 @@ You can also take some **optional** further steps:
files to your project, add, commit and push to GitLab.
1. From the your **Project**'s page, click **Set up CI/CD**:
- ![setup GitLab CI/CD](img/setup_ci.png)
+ ![setup GitLab CI/CD](img/setup_ci.png)
1. Choose one of the templates from the dropbox menu.
Pick up the template corresponding to the SSG you're using (or plain HTML).
- ![gitlab-ci templates](img/choose_ci_template.png)
+ ![gitlab-ci templates](img/choose_ci_template.png)
Once you have both site files and `.gitlab-ci.yml` in your project's
root, GitLab CI/CD will build your site and deploy it with Pages.
@@ -123,20 +123,20 @@ where you'll find its default URL.
> **Notes:**
>
> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
- if you don't find yours among the templates, you'll need
- to configure your own `.gitlab-ci.yml`. To do that, please
- read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
- the [example projects](https://gitlab.com/pages). If you set
- up a new one, please
- [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
- to our examples.
+> if you don't find yours among the templates, you'll need
+> to configure your own `.gitlab-ci.yml`. To do that, please
+> read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
+> the [example projects](https://gitlab.com/pages). If you set
+> up a new one, please
+> [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
+> to our examples.
>
> - The second step _"Clone it to your local computer"_, can be done
- differently, achieving the same results: instead of cloning the bare
- repository to you local computer and moving your site files into it,
- you can run `git init` in your local website directory, add the
- remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
- then add, commit, and push to GitLab.
+> differently, achieving the same results: instead of cloning the bare
+> repository to you local computer and moving your site files into it,
+> you can run `git init` in your local website directory, add the
+> remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
+> then add, commit, and push to GitLab.
## URLs and Baseurls
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 25944b029d7..a0bc10b0ed7 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -101,7 +101,7 @@ To get started with GitLab Pages, you can either:
1. Select **Create from Template**.
1. Choose one of the templates starting with **Pages**:
- ![Project templates for Pages](img/pages_project_templates_v11_8.png)
+ ![Project templates for Pages](img/pages_project_templates_v11_8.png)
1. From the left sidebar, navigate to your project's **CI/CD > Pipelines**
and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index e3515711f17..7a79cdbcaee 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -34,11 +34,11 @@ that the `master` branch is protected by default.
1. From the **Branch** dropdown menu, select the branch you want to protect and
click **Protect**. In the screenshot below, we chose the `develop` branch.
- ![Protected branches page](img/protected_branches_page.png)
+ ![Protected branches page](img/protected_branches_page.png)
1. Once done, the protected branch will appear in the "Protected branches" list.
- ![Protected branches list](img/protected_branches_list.png)
+ ![Protected branches list](img/protected_branches_list.png)
## Using the Allowed to merge and Allowed to push settings
@@ -145,7 +145,7 @@ branches via GitLab's web interface:
1. In order to prevent accidental deletion, an additional confirmation is
required
- ![Delete protected branches](img/protected_branches_delete.png)
+ ![Delete protected branches](img/protected_branches_delete.png)
Deleting a protected branch is only allowed via the web interface, not via Git.
This means that you can't accidentally delete a protected branch from your
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 687c4e1ea6f..5cc3b8a7fc3 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -20,19 +20,19 @@ To protect a tag, you need to have at least Maintainer permission level.
1. Navigate to the project's **Settings > Repository**:
- ![Repository Settings](img/project_repository_settings.png)
+ ![Repository Settings](img/project_repository_settings.png)
1. From the **Tag** dropdown menu, select the tag you want to protect or type and click **Create wildcard**. In the screenshot below, we chose to protect all tags matching `v*`:
- ![Protected tags page](img/protected_tags_page.png)
+ ![Protected tags page](img/protected_tags_page.png)
1. From the **Allowed to create** dropdown, select who will have permission to create matching tags and then click **Protect**:
- ![Allowed to create tags dropdown](img/protected_tags_permissions_dropdown.png)
+ ![Allowed to create tags dropdown](img/protected_tags_permissions_dropdown.png)
1. Once done, the protected tag will appear in the **Protected tags** list:
- ![Protected tags list](img/protected_tags_list.png)
+ ![Protected tags list](img/protected_tags_list.png)
## Wildcard protected tags
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 437899dce1e..6758adf2b43 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -77,8 +77,8 @@ you manually enter a parameter, it must be enclosed in double quotation marks
(`"`), unless it contains only:
1. ASCII letters.
-2. Numerals.
-3. Underscore, hyphen, question mark, dot, and ampersand.
+1. Numerals.
+1. Underscore, hyphen, question mark, dot, and ampersand.
Parameters are also case-sensitive. Autocomplete handles this, and the insertion
of quotation marks, automatically.
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index 09a5cdabc00..b5299eaca27 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -105,7 +105,7 @@ Once you click it, a new branch will be created that diverges from the default
branch of your project, by default `master`. The branch name will be based on
the title of the issue and as a prefix, it will have its internal ID. Thus, the example
screenshot above will yield a branch named
-`2-et-cum-et-sed-expedita-repellat-consequatur-ut-assumenda-numquam-rerum`.
+`23177-add-support-for-rich-references-to-referables`.
Since GitLab 9.0, when you click the `New branch` in an empty repository project, GitLab automatically creates the master branch, commits a blank `README.md` file to it and creates and redirects you to a new branch based on the issue title.
If your [project is already configured with a deployment service][project-services-doc] (e.g. Kubernetes), GitLab takes one step further and prompts you to set up [auto deploy][auto-deploy-doc] by helping you create a `.gitlab-ci.yml` file.
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index d61d7eafd18..5d08bf5e77d 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -84,6 +84,8 @@ You can see GitLab's keyboard shortcuts by using <kbd>shift</kbd> + <kbd>?</kbd>
| <kbd>l</kbd> | Change label |
| <kbd>]</kbd> or <kbd>j</kbd> | Move to next file |
| <kbd>[</kbd> or <kbd>k</kbd> | Move to previous file |
+| <kbd>n</kbd> | Move to next unresolved discussion |
+| <kbd>p</kbd> | Move to previous unresolved discussion |
## Epics **(ULTIMATE)**
diff --git a/lib/bitbucket/representation/comment.rb b/lib/bitbucket/representation/comment.rb
index 1b8dc27793a..127598a76ab 100644
--- a/lib/bitbucket/representation/comment.rb
+++ b/lib/bitbucket/representation/comment.rb
@@ -4,7 +4,7 @@ module Bitbucket
module Representation
class Comment < Representation::Base
def author
- user['username']
+ user['nickname']
end
def note
diff --git a/lib/bitbucket/representation/issue.rb b/lib/bitbucket/representation/issue.rb
index a88797cdab9..3f6db9cb75b 100644
--- a/lib/bitbucket/representation/issue.rb
+++ b/lib/bitbucket/representation/issue.rb
@@ -14,7 +14,7 @@ module Bitbucket
end
def author
- raw.dig('reporter', 'username')
+ raw.dig('reporter', 'nickname')
end
def description
diff --git a/lib/bitbucket/representation/pull_request.rb b/lib/bitbucket/representation/pull_request.rb
index 6a0e8b354bf..a498c9bc213 100644
--- a/lib/bitbucket/representation/pull_request.rb
+++ b/lib/bitbucket/representation/pull_request.rb
@@ -4,7 +4,7 @@ module Bitbucket
module Representation
class PullRequest < Representation::Base
def author
- raw.fetch('author', {}).fetch('username', nil)
+ raw.fetch('author', {}).fetch('nickname', nil)
end
def description
diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb
index 6a608058813..cf55c692271 100644
--- a/lib/bitbucket_server/client.rb
+++ b/lib/bitbucket_server/client.rb
@@ -23,8 +23,9 @@ module BitbucketServer
BitbucketServer::Representation::Repo.new(parsed_response)
end
- def repos(page_offset: 0, limit: nil)
+ def repos(page_offset: 0, limit: nil, filter: nil)
path = "/repos"
+ path += "?name=#{filter}" if filter
get_collection(path, :repo, page_offset: page_offset, limit: limit)
end
diff --git a/lib/feature/gitaly.rb b/lib/feature/gitaly.rb
index edfd2fb17f3..9ded1aed4e3 100644
--- a/lib/feature/gitaly.rb
+++ b/lib/feature/gitaly.rb
@@ -7,7 +7,7 @@ class Feature
# Server feature flags should use '_' to separate words.
SERVER_FEATURE_FLAGS =
[
- 'get_commit_signatures'.freeze
+ # 'get_commit_signatures'.freeze
].freeze
DEFAULT_ON_FLAGS = Set.new([]).freeze
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 8047ef4fa15..24bc73e0de5 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -262,13 +262,19 @@ module Gitlab
def pull_request_comment_attributes(comment)
{
project: project,
- note: comment.note,
author_id: gitlab_user_id(project, comment.author),
+ note: comment_note(comment),
created_at: comment.created_at,
updated_at: comment.updated_at
}
end
+ def comment_note(comment)
+ author = @formatter.author_line(comment.author) unless find_user_id(comment.author)
+
+ author.to_s + comment.note.to_s
+ end
+
def log_error(details)
logger.error(log_base_data.merge(details))
end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 382b8896dbd..b7886114e9c 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -218,7 +218,7 @@ module Gitlab
return if @sections.include?(section)
@sections << section
- write_raw %{<div class="js-section-start fa fa-caret-down pr-2 cursor-pointer" data-timestamp="#{timestamp}" data-section="#{data_section_names}" role="button"></div>}
+ write_raw %{<div class="js-section-start section-start fa fa-caret-down pr-2 cursor-pointer" data-timestamp="#{timestamp}" data-section="#{data_section_names}" role="button"></div>}
@lineno_in_section = 0
end
diff --git a/lib/gitlab/cluster/puma_worker_killer_observer.rb b/lib/gitlab/cluster/puma_worker_killer_observer.rb
index 3b4ebc3fbae..f53051c32ff 100644
--- a/lib/gitlab/cluster/puma_worker_killer_observer.rb
+++ b/lib/gitlab/cluster/puma_worker_killer_observer.rb
@@ -15,9 +15,7 @@ module Gitlab
private
def log_termination(worker)
- labels = { worker: "worker_#{worker.index}" }
-
- @counter.increment(labels)
+ @counter.increment
end
end
end
diff --git a/lib/gitlab/gitaly_client/notification_service.rb b/lib/gitlab/gitaly_client/notification_service.rb
deleted file mode 100644
index 873c3e4086d..00000000000
--- a/lib/gitlab/gitaly_client/notification_service.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module GitalyClient
- class NotificationService
- # 'repository' is a Gitlab::Git::Repository
- def initialize(repository)
- @gitaly_repo = repository.gitaly_repository
- @storage = repository.storage
- end
-
- def post_receive
- GitalyClient.call(
- @storage,
- :notification_service,
- :post_receive,
- Gitaly::PostReceiveRequest.new(repository: @gitaly_repo)
- )
- end
- end
- end
-end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index f3888857bb6..1b7fc5fa10f 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -37,11 +37,11 @@ project_tree:
- :user
- merge_requests:
- :metrics
- - :suggestions
- notes:
- :author
- events:
- :push_event_payload
+ - :suggestions
- merge_request_diff:
- :merge_request_diff_commits
- :merge_request_diff_files
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 5e77d31760d..2669adb8455 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -29,6 +29,21 @@ module Gitlab
end
end
+ def formatted_count(scope)
+ case scope
+ when 'blobs'
+ blobs_count.to_s
+ when 'notes'
+ formatted_limited_count(limited_notes_count)
+ when 'wiki_blobs'
+ wiki_blobs_count.to_s
+ when 'commits'
+ commits_count.to_s
+ else
+ super
+ end
+ end
+
def users
super.where(id: @project.team.members) # rubocop:disable CodeReuse/ActiveRecord
end
diff --git a/lib/gitlab/quick_actions/issuable_actions.rb b/lib/gitlab/quick_actions/issuable_actions.rb
index b975a967d03..e5d99ebee35 100644
--- a/lib/gitlab/quick_actions/issuable_actions.rb
+++ b/lib/gitlab/quick_actions/issuable_actions.rb
@@ -145,9 +145,9 @@ module Gitlab
run_label_command(labels: find_labels(labels_param), command: :relabel, updates_key: :label_ids)
end
- desc _('Add a todo')
- explanation _('Adds a todo.')
- execution_message _('Added a todo.')
+ desc _('Add a To Do')
+ explanation _('Adds a To Do.')
+ execution_message _('Added a To Do.')
types Issuable
condition do
quick_action_target.persisted? &&
@@ -157,9 +157,9 @@ module Gitlab
@updates[:todo_event] = 'add'
end
- desc _('Mark to do as done')
- explanation _('Marks to do as done.')
- execution_message _('Marked to do as done.')
+ desc _('Mark To Do as done')
+ explanation _('Marks To Do as done.')
+ execution_message _('Marked To Do as done.')
types Issuable
condition do
quick_action_target.persisted? &&
diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb
index 0868bd85600..da28fbf5be0 100644
--- a/lib/gitlab/quick_actions/issue_actions.rb
+++ b/lib/gitlab/quick_actions/issue_actions.rb
@@ -57,19 +57,18 @@ module Gitlab
labels = find_labels(target_list_name)
label_ids = labels.map(&:id)
- if label_ids.size == 1
+ if label_ids.size > 1
+ message = _('Failed to move this issue because only a single label can be provided.')
+ elsif !Label.on_project_board?(quick_action_target.project_id, label_ids.first)
+ message = _('Failed to move this issue because label was not found.')
+ else
label_id = label_ids.first
- # Ensure this label corresponds to a list on the board
- next unless Label.on_project_board?(quick_action_target.project_id, label_id)
-
@updates[:remove_label_ids] =
quick_action_target.labels.on_project_boards(quick_action_target.project_id).where.not(id: label_id).pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
@updates[:add_label_ids] = [label_id]
message = _("Moved issue to %{label} column in the board.") % { label: labels_to_reference(labels).first }
- else
- message = _('Move this issue failed because you need to specify only one label.')
end
@execution_message[:board_move] = message
@@ -93,7 +92,7 @@ module Gitlab
message = _("Marked this issue as a duplicate of %{duplicate_param}.") % { duplicate_param: duplicate_param }
else
- message = _('Mark as duplicate failed because referenced issue was not found')
+ message = _('Failed to mark this issue as a duplicate because referenced issue was not found.')
end
@execution_message[:duplicate] = message
@@ -117,18 +116,18 @@ module Gitlab
message = _("Moved this issue to %{path_to_project}.") % { path_to_project: target_project_path }
else
- message = _("Move this issue failed because target project doesn't exists")
+ message = _("Failed to move this issue because target project doesn't exist.")
end
@execution_message[:move] = message
end
- desc _('Make issue confidential.')
+ desc _('Make issue confidential')
explanation do
- _('Makes this issue confidential')
+ _('Makes this issue confidential.')
end
execution_message do
- _('Made this issue confidential')
+ _('Made this issue confidential.')
end
types Issue
condition do
@@ -138,19 +137,19 @@ module Gitlab
@updates[:confidential] = true
end
- desc _('Create a merge request.')
+ desc _('Create a merge request')
explanation do |branch_name = nil|
if branch_name
- _("Creates branch '%{branch_name}' and a merge request to resolve this issue") % { branch_name: branch_name }
+ _("Creates branch '%{branch_name}' and a merge request to resolve this issue.") % { branch_name: branch_name }
else
- _('Creates a branch and a merge request to resolve this issue')
+ _('Creates a branch and a merge request to resolve this issue.')
end
end
execution_message do |branch_name = nil|
if branch_name
- _("Created branch '%{branch_name}' and a merge request to resolve this issue") % { branch_name: branch_name }
+ _("Created branch '%{branch_name}' and a merge request to resolve this issue.") % { branch_name: branch_name }
else
- _('Created a branch and a merge request to resolve this issue')
+ _('Created a branch and a merge request to resolve this issue.')
end
end
params "<branch name>"
diff --git a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
index 41ffd51cde8..533c74ba9b4 100644
--- a/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
+++ b/lib/gitlab/quick_actions/issue_and_merge_request_actions.rb
@@ -24,7 +24,7 @@ module Gitlab
end
command :assign do |users|
if users.empty?
- @execution_message[:assign] = _("Assign command failed because no user was found")
+ @execution_message[:assign] = _("Failed to assign a user because no user was found.")
next
end
@@ -211,8 +211,8 @@ module Gitlab
end
desc _("Lock the discussion")
- explanation _("Locks the discussion")
- execution_message _("Locked the discussion")
+ explanation _("Locks the discussion.")
+ execution_message _("Locked the discussion.")
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
@@ -224,8 +224,8 @@ module Gitlab
end
desc _("Unlock the discussion")
- explanation _("Unlocks the discussion")
- execution_message _("Unlocked the discussion")
+ explanation _("Unlocks the discussion.")
+ execution_message _("Unlocked the discussion.")
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 7c1e6b1baff..ce4c1611687 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -43,6 +43,29 @@ module Gitlab
without_count ? collection.without_count : collection
end
+ def formatted_count(scope)
+ case scope
+ when 'projects'
+ formatted_limited_count(limited_projects_count)
+ when 'issues'
+ formatted_limited_count(limited_issues_count)
+ when 'merge_requests'
+ formatted_limited_count(limited_merge_requests_count)
+ when 'milestones'
+ formatted_limited_count(limited_milestones_count)
+ when 'users'
+ formatted_limited_count(limited_users_count)
+ end
+ end
+
+ def formatted_limited_count(count)
+ if count >= COUNT_LIMIT
+ "#{COUNT_LIMIT - 1}+"
+ else
+ count.to_s
+ end
+ end
+
def limited_projects_count
@limited_projects_count ||= limited_count(projects)
end
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index e360b552f89..ac3b219e0c7 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -22,6 +22,17 @@ module Gitlab
end
end
+ def formatted_count(scope)
+ case scope
+ when 'snippet_titles'
+ snippet_titles_count.to_s
+ when 'snippet_blobs'
+ snippet_blobs_count.to_s
+ else
+ super
+ end
+ end
+
def snippet_titles_count
@snippet_titles_count ||= snippet_titles.count
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 50103c226d4..d33c62031c4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -300,6 +300,9 @@ msgstr ""
msgid "'%{source}' is not a import source"
msgstr ""
+msgid "(Show all)"
+msgstr ""
+
msgid "(external source)"
msgstr ""
@@ -672,9 +675,6 @@ msgstr ""
msgid "Add a task list"
msgstr ""
-msgid "Add a todo"
-msgstr ""
-
msgid "Add an SSH key"
msgstr ""
@@ -714,6 +714,9 @@ msgstr ""
msgid "Add new directory"
msgstr ""
+msgid "Add new member to %{strong_start}%{group_name}%{strong_end}"
+msgstr ""
+
msgid "Add or subtract spent time"
msgstr ""
@@ -747,7 +750,7 @@ msgstr ""
msgid "Added %{label_references} %{label_text}."
msgstr ""
-msgid "Added a todo."
+msgid "Added a To Do."
msgstr ""
msgid "Added at"
@@ -762,7 +765,7 @@ msgstr ""
msgid "Adds %{labels} %{label_text}."
msgstr ""
-msgid "Adds a todo."
+msgid "Adds a To Do."
msgstr ""
msgid "Admin Area"
@@ -1359,9 +1362,6 @@ msgstr ""
msgid "Assign"
msgstr ""
-msgid "Assign command failed because no user was found"
-msgstr ""
-
msgid "Assign custom color like #FF0000"
msgstr ""
@@ -1945,6 +1945,9 @@ msgstr ""
msgid "CICD|instance enabled"
msgstr ""
+msgid "CLOSED"
+msgstr ""
+
msgid "CONTRIBUTING"
msgstr ""
@@ -2005,6 +2008,15 @@ msgstr ""
msgid "Certificate (PEM)"
msgstr ""
+msgid "Change Label"
+msgstr ""
+
+msgid "Change assignee"
+msgstr ""
+
+msgid "Change milestone"
+msgstr ""
+
msgid "Change path"
msgstr ""
@@ -2950,9 +2962,6 @@ msgstr ""
msgid "Commands applied"
msgstr ""
-msgid "Commands did not apply"
-msgstr ""
-
msgid "Comment"
msgstr ""
@@ -2985,6 +2994,9 @@ msgstr[1] ""
msgid "Commit %{commit_id}"
msgstr ""
+msgid "Commit (when editing commit message)"
+msgstr ""
+
msgid "Commit Message"
msgstr ""
@@ -3375,7 +3387,7 @@ msgstr ""
msgid "Create a GitLab account first, and then connect it to your %{label} account."
msgstr ""
-msgid "Create a merge request."
+msgid "Create a merge request"
msgstr ""
msgid "Create a new branch"
@@ -3483,10 +3495,10 @@ msgstr ""
msgid "Created At"
msgstr ""
-msgid "Created a branch and a merge request to resolve this issue"
+msgid "Created a branch and a merge request to resolve this issue."
msgstr ""
-msgid "Created branch '%{branch_name}' and a merge request to resolve this issue"
+msgid "Created branch '%{branch_name}' and a merge request to resolve this issue."
msgstr ""
msgid "Created by me"
@@ -3498,10 +3510,10 @@ msgstr ""
msgid "Created on:"
msgstr ""
-msgid "Creates a branch and a merge request to resolve this issue"
+msgid "Creates a branch and a merge request to resolve this issue."
msgstr ""
-msgid "Creates branch '%{branch_name}' and a merge request to resolve this issue"
+msgid "Creates branch '%{branch_name}' and a merge request to resolve this issue."
msgstr ""
msgid "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
@@ -3899,6 +3911,9 @@ msgstr ""
msgid "Diffs|No file name available"
msgstr ""
+msgid "Diffs|Show all lines"
+msgstr ""
+
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr ""
@@ -4094,12 +4109,24 @@ msgstr ""
msgid "Edit identity for %{user_name}"
msgstr ""
+msgid "Edit issue"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
+msgid "Edit last comment (when focused on an empty textarea)"
+msgstr ""
+
+msgid "Edit merge request"
+msgstr ""
+
msgid "Edit public deploy key"
msgstr ""
+msgid "Edit wiki page"
+msgstr ""
+
msgid "Email"
msgstr ""
@@ -4640,6 +4667,9 @@ msgstr ""
msgid "Except policy:"
msgstr ""
+msgid "Existing"
+msgstr ""
+
msgid "Existing members and groups"
msgstr ""
@@ -4649,12 +4679,18 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand down"
+msgstr ""
+
msgid "Expand dropdown"
msgstr ""
msgid "Expand sidebar"
msgstr ""
+msgid "Expand up"
+msgstr ""
+
msgid "Expiration date"
msgstr ""
@@ -4754,6 +4790,12 @@ msgstr ""
msgid "Failed create wiki"
msgstr ""
+msgid "Failed to apply commands."
+msgstr ""
+
+msgid "Failed to assign a user because no user was found."
+msgstr ""
+
msgid "Failed to change the owner"
msgstr ""
@@ -4796,6 +4838,18 @@ msgstr ""
msgid "Failed to load related branches"
msgstr ""
+msgid "Failed to mark this issue as a duplicate because referenced issue was not found."
+msgstr ""
+
+msgid "Failed to move this issue because label was not found."
+msgstr ""
+
+msgid "Failed to move this issue because only a single label can be provided."
+msgstr ""
+
+msgid "Failed to move this issue because target project doesn't exist."
+msgstr ""
+
msgid "Failed to promote label due to internal error. Please contact administrators."
msgstr ""
@@ -4921,6 +4975,9 @@ msgstr ""
msgid "Filter results by project"
msgstr ""
+msgid "Filter your projects by name"
+msgstr ""
+
msgid "Filter..."
msgstr ""
@@ -4939,6 +4996,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Finding Project File"
+msgstr ""
+
msgid "Fingerprint"
msgstr ""
@@ -4963,6 +5023,12 @@ msgstr ""
msgid "FlowdockService|Flowdock is a collaboration web app for technical teams."
msgstr ""
+msgid "Focus Filter"
+msgstr ""
+
+msgid "Focus Search"
+msgstr ""
+
msgid "FogBugz Email"
msgstr ""
@@ -5182,6 +5248,9 @@ msgstr ""
msgid "Given access %{time_ago}"
msgstr ""
+msgid "Global Shortcuts"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -5206,12 +5275,81 @@ msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to commits"
+msgstr ""
+
+msgid "Go to environments"
+msgstr ""
+
+msgid "Go to file"
+msgstr ""
+
+msgid "Go to file permalink"
+msgstr ""
+
+msgid "Go to files"
+msgstr ""
+
+msgid "Go to finding file"
+msgstr ""
+
+msgid "Go to groups"
+msgstr ""
+
+msgid "Go to issue boards"
+msgstr ""
+
+msgid "Go to issues"
+msgstr ""
+
+msgid "Go to jobs"
+msgstr ""
+
+msgid "Go to kubernetes"
+msgstr ""
+
+msgid "Go to merge requests"
+msgstr ""
+
+msgid "Go to metrics"
+msgstr ""
+
+msgid "Go to milestones"
+msgstr ""
+
+msgid "Go to network graph"
+msgstr ""
+
msgid "Go to parent"
msgstr ""
msgid "Go to project"
msgstr ""
+msgid "Go to projects"
+msgstr ""
+
+msgid "Go to repository charts"
+msgstr ""
+
+msgid "Go to snippets"
+msgstr ""
+
+msgid "Go to the activity feed"
+msgstr ""
+
+msgid "Go to the project's activity feed"
+msgstr ""
+
+msgid "Go to the project's overview page"
+msgstr ""
+
+msgid "Go to todos"
+msgstr ""
+
+msgid "Go to wiki"
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
@@ -6147,6 +6285,9 @@ msgstr ""
msgid "Key: %{key}"
msgstr ""
+msgid "Keyboard Shortcuts"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -6430,13 +6571,13 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
-msgid "Locked the discussion"
+msgid "Locked the discussion."
msgstr ""
msgid "Locked to current projects"
msgstr ""
-msgid "Locks the discussion"
+msgid "Locks the discussion."
msgstr ""
msgid "Logo was successfully removed."
@@ -6445,19 +6586,22 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MERGED"
+msgstr ""
+
msgid "MRDiff|Show changes only"
msgstr ""
msgid "MRDiff|Show full file"
msgstr ""
-msgid "Made this issue confidential"
+msgid "Made this issue confidential."
msgstr ""
msgid "Make and review changes in the browser with the Web IDE"
msgstr ""
-msgid "Make issue confidential."
+msgid "Make issue confidential"
msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
@@ -6466,7 +6610,7 @@ msgstr ""
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
msgstr ""
-msgid "Makes this issue confidential"
+msgid "Makes this issue confidential."
msgstr ""
msgid "Manage"
@@ -6532,10 +6676,10 @@ msgstr ""
msgid "March"
msgstr ""
-msgid "Mark as done"
+msgid "Mark To Do as done"
msgstr ""
-msgid "Mark as duplicate failed because referenced issue was not found"
+msgid "Mark as done"
msgstr ""
msgid "Mark as resolved"
@@ -6547,9 +6691,6 @@ msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
-msgid "Mark to do as done"
-msgstr ""
-
msgid "Markdown"
msgstr ""
@@ -6562,13 +6703,16 @@ msgstr ""
msgid "Markdown is supported"
msgstr ""
+msgid "Marked To Do as done."
+msgstr ""
+
msgid "Marked this %{noun} as Work In Progress."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr ""
-msgid "Marked to do as done."
+msgid "Marks To Do as done."
msgstr ""
msgid "Marks this %{noun} as Work In Progress."
@@ -6577,9 +6721,6 @@ msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr ""
-msgid "Marks to do as done."
-msgstr ""
-
msgid "Max access level"
msgstr ""
@@ -6616,6 +6757,12 @@ msgstr ""
msgid "Members of <strong>%{project_name}</strong>"
msgstr ""
+msgid "Members with access to %{strong_start}%{group_name}%{strong_end}"
+msgstr ""
+
+msgid "Members with pending access to %{strong_start}%{group_name}%{strong_end}"
+msgstr ""
+
msgid "Merge"
msgstr ""
@@ -6934,15 +7081,27 @@ msgstr ""
msgid "Move issue from one column of the board to another"
msgstr ""
-msgid "Move this issue failed because target project doesn't exists"
+msgid "Move selection down"
msgstr ""
-msgid "Move this issue failed because you need to specify only one label."
+msgid "Move selection up"
msgstr ""
msgid "Move this issue to another project."
msgstr ""
+msgid "Move to next file"
+msgstr ""
+
+msgid "Move to next unresolved discussion"
+msgstr ""
+
+msgid "Move to previous file"
+msgstr ""
+
+msgid "Move to previous unresolved discussion"
+msgstr ""
+
msgid "MoveIssue|Cannot move issue due to insufficient permissions!"
msgstr ""
@@ -7003,6 +7162,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Network Graph"
+msgstr ""
+
msgid "Never"
msgstr ""
@@ -7463,6 +7625,9 @@ msgstr ""
msgid "Open Documentation"
msgstr ""
+msgid "Open Selection"
+msgstr ""
+
msgid "Open comment type dropdown"
msgstr ""
@@ -8468,6 +8633,12 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project File"
+msgstr ""
+
+msgid "Project Files browsing"
+msgstr ""
+
msgid "Project ID"
msgstr ""
@@ -9220,6 +9391,9 @@ msgstr ""
msgid "Replaced all labels with %{label_references} %{label_text}."
msgstr ""
+msgid "Reply (quoting selected text)"
+msgstr ""
+
msgid "Reply by email"
msgstr ""
@@ -9582,15 +9756,27 @@ msgstr ""
msgid "Scopes"
msgstr ""
+msgid "Scroll down"
+msgstr ""
+
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
msgstr ""
+msgid "Scroll left"
+msgstr ""
+
+msgid "Scroll right"
+msgstr ""
+
msgid "Scroll to bottom"
msgstr ""
msgid "Scroll to top"
msgstr ""
+msgid "Scroll up"
+msgstr ""
+
msgid "Search"
msgstr ""
@@ -10035,6 +10221,9 @@ msgstr ""
msgid "Show whitespace changes"
msgstr ""
+msgid "Show/hide this dialog"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
@@ -11750,6 +11939,9 @@ msgstr ""
msgid "Today"
msgstr ""
+msgid "Toggle Markdown preview"
+msgstr ""
+
msgid "Toggle Sidebar"
msgstr ""
@@ -11774,6 +11966,9 @@ msgstr ""
msgid "Toggle sidebar"
msgstr ""
+msgid "Toggle the Performance Bar"
+msgstr ""
+
msgid "Toggle thread"
msgstr ""
@@ -11993,10 +12188,10 @@ msgstr ""
msgid "Unlocked"
msgstr ""
-msgid "Unlocked the discussion"
+msgid "Unlocked the discussion."
msgstr ""
-msgid "Unlocks the discussion"
+msgid "Unlocks the discussion."
msgstr ""
msgid "Unmarked this %{noun} as Work In Progress."
@@ -12535,6 +12730,9 @@ msgstr ""
msgid "Wiki"
msgstr ""
+msgid "Wiki pages"
+msgstr ""
+
msgid "Wiki was successfully updated."
msgstr ""
@@ -13750,6 +13948,9 @@ msgstr ""
msgid "updated"
msgstr ""
+msgid "updated %{time_ago}"
+msgstr ""
+
msgid "user avatar"
msgstr ""
diff --git a/package.json b/package.json
index 7ff4e74137b..803aebcb5fd 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.67.0",
- "@gitlab/ui": "5.14.0",
+ "@gitlab/ui": "5.15.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-link": "^1.2.11",
diff --git a/qa/Gemfile b/qa/Gemfile
index 53e7cc497e2..6abc0d622ad 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -8,7 +8,7 @@ gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
-gem 'nokogiri', '~> 1.10.3'
+gem 'nokogiri', '~> 1.10.4'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_junit_formatter', '~> 0.4.1'
gem 'faker', '~> 1.6', '>= 1.6.6'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 7d19366f83b..bf051a115b5 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -52,7 +52,7 @@ GEM
mini_portile2 (2.4.0)
minitest (5.11.1)
netrc (0.11.0)
- nokogiri (1.10.3)
+ nokogiri (1.10.4)
mini_portile2 (~> 2.4.0)
parallel (1.17.0)
parallel_tests (2.29.0)
@@ -112,13 +112,13 @@ DEPENDENCIES
faker (~> 1.6, >= 1.6.6)
gitlab-qa
knapsack (~> 1.17)
- nokogiri (~> 1.10.3)
+ nokogiri (~> 1.10.4)
parallel_tests (~> 2.29)
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
rspec-retry (~> 0.6.1)
- rspec_junit_formatter (~> 0.4.1)
+ rspec_junit_formatter (~> 0.4.1)
selenium-webdriver (~> 3.12)
BUNDLED WITH
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
index 680c5e21fa4..f915d412bf3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
@@ -2,8 +2,7 @@
module QA
context 'Create' do
- # failure reported: https://gitlab.com/gitlab-org/quality/nightly/issues/42
- describe 'Commit data', :quarantine do
+ describe 'Commit data' do
before(:context) do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index 413598ddde0..908c564e761 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -16,6 +16,39 @@ describe Groups::GroupMembersController do
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:index)
end
+
+ context 'user with owner access' do
+ let!(:invited) { create_list(:group_member, 3, :invited, group: group) }
+
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ it 'assigns invited members' do
+ get :index, params: { group_id: group }
+
+ expect(assigns(:invited_members).map(&:invite_email)).to match_array(invited.map(&:invite_email))
+ end
+
+ it 'restricts search to one email' do
+ get :index, params: { group_id: group, search_invited: invited.first.invite_email }
+
+ expect(assigns(:invited_members).map(&:invite_email)).to match_array(invited.first.invite_email)
+ end
+
+ it 'paginates invited list' do
+ stub_const('Groups::GroupMembersController::MEMBER_PER_PAGE_LIMIT', 2)
+
+ get :index, params: { group_id: group, invited_members_page: 1 }
+
+ expect(assigns(:invited_members).count).to eq(2)
+
+ get :index, params: { group_id: group, invited_members_page: 2 }
+
+ expect(assigns(:invited_members).count).to eq(1)
+ end
+ end
end
describe 'POST create' do
diff --git a/spec/controllers/import/bitbucket_server_controller_spec.rb b/spec/controllers/import/bitbucket_server_controller_spec.rb
index b89d7317b9c..e1aeab46fca 100644
--- a/spec/controllers/import/bitbucket_server_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_server_controller_spec.rb
@@ -134,6 +134,8 @@ describe Import::BitbucketServerController do
describe 'GET status' do
render_views
+ let(:repos) { instance_double(BitbucketServer::Collection) }
+
before do
allow(controller).to receive(:bitbucket_client).and_return(client)
@@ -145,7 +147,6 @@ describe Import::BitbucketServerController do
it 'assigns repository categories' do
created_project = create(:project, :import_finished, import_type: 'bitbucket_server', creator_id: user.id, import_source: @created_repo.browse_url)
- repos = instance_double(BitbucketServer::Collection)
expect(repos).to receive(:partition).and_return([[@repo, @created_repo], [@invalid_repo]])
expect(repos).to receive(:current_page).and_return(1)
@@ -159,6 +160,17 @@ describe Import::BitbucketServerController do
expect(assigns(:repos)).to eq([@repo])
expect(assigns(:incompatible_repos)).to eq([@invalid_repo])
end
+
+ context 'when filtering' do
+ let(:filter) { 'test' }
+
+ it 'passes filter param to bitbucket client' do
+ expect(repos).to receive(:partition).and_return([[@repo, @created_repo], [@invalid_repo]])
+ expect(client).to receive(:repos).with(filter: filter, limit: 25, page_offset: 0).and_return(repos)
+
+ get :status, params: { filter: filter }, as: :json
+ end
+ end
end
describe 'GET jobs' do
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 5a5c0a1f6ac..3e0d53a6573 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -11,151 +11,173 @@ describe SearchController do
sign_in(user)
end
- context 'uses the right partials depending on scope' do
- using RSpec::Parameterized::TableSyntax
- render_views
-
- set(:project) { create(:project, :public, :repository, :wiki_repo) }
-
+ shared_examples_for 'when the user cannot read cross project' do |action, params|
before do
- expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_cross_project, :global) { false }
end
- subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) }
+ it 'blocks access without a project_id' do
+ get action, params: params
- where(:partial, :scope) do
- '_blob' | :blobs
- '_wiki_blob' | :wiki_blobs
- '_commit' | :commits
+ expect(response).to have_gitlab_http_status(403)
end
- with_them do
- it do
- project_wiki = create(:project_wiki, project: project, user: user)
- create(:wiki_page, wiki: project_wiki, attrs: { title: 'merge', content: 'merge' })
+ it 'allows access with a project_id' do
+ get action, params: params.merge(project_id: create(:project, :public).id)
- expect(subject).to render_template("search/results/#{partial}")
- end
+ expect(response).to have_gitlab_http_status(200)
end
end
- context 'global search' do
- render_views
-
- it 'omits pipeline status from load' do
- project = create(:project, :public)
- expect(Gitlab::Cache::Ci::ProjectPipelineStatus).not_to receive(:load_in_batch_for_projects)
-
- get :show, params: { scope: 'projects', search: project.name }
+ shared_examples_for 'with external authorization service enabled' do |action, params|
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:note) { create(:note_on_issue, project: project) }
- expect(assigns[:search_objects].first).to eq project
+ before do
+ enable_external_authorization_service_check
end
- end
-
- it 'finds issue comments' do
- project = create(:project, :public)
- note = create(:note_on_issue, project: project)
- get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
+ it 'renders a 403 when no project is given' do
+ get action, params: params
- expect(assigns[:search_objects].first).to eq note
- end
-
- context 'when the user cannot read cross project' do
- before do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?)
- .with(user, :read_cross_project, :global) { false }
+ expect(response).to have_gitlab_http_status(403)
end
- it 'still allows accessing the search page' do
- get :show
+ it 'renders a 200 when a project was set' do
+ get action, params: params.merge(project_id: project.id)
expect(response).to have_gitlab_http_status(200)
end
+ end
- it 'still blocks searches without a project_id' do
- get :show, params: { search: 'hello' }
+ describe 'GET #show' do
+ it_behaves_like 'when the user cannot read cross project', :show, { search: 'hello' } do
+ it 'still allows accessing the search page' do
+ get :show
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(200)
+ end
end
- it 'allows searches with a project_id' do
- get :show, params: { search: 'hello', project_id: create(:project, :public).id }
+ it_behaves_like 'with external authorization service enabled', :show, { search: 'hello' }
- expect(response).to have_gitlab_http_status(200)
- end
- end
+ context 'uses the right partials depending on scope' do
+ using RSpec::Parameterized::TableSyntax
+ render_views
+
+ set(:project) { create(:project, :public, :repository, :wiki_repo) }
- context 'on restricted projects' do
- context 'when signed out' do
before do
- sign_out(user)
+ expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original
end
- it "doesn't expose comments on issues" do
- project = create(:project, :public, :issues_private)
- note = create(:note_on_issue, project: project)
+ subject { get(:show, params: { project_id: project.id, scope: scope, search: 'merge' }) }
- get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
+ where(:partial, :scope) do
+ '_blob' | :blobs
+ '_wiki_blob' | :wiki_blobs
+ '_commit' | :commits
+ end
- expect(assigns[:search_objects].count).to eq(0)
+ with_them do
+ it do
+ project_wiki = create(:project_wiki, project: project, user: user)
+ create(:wiki_page, wiki: project_wiki, attrs: { title: 'merge', content: 'merge' })
+
+ expect(subject).to render_template("search/results/#{partial}")
+ end
end
end
- it "doesn't expose comments on merge_requests" do
- project = create(:project, :public, :merge_requests_private)
- note = create(:note_on_merge_request, project: project)
+ context 'global search' do
+ render_views
- get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
+ it 'omits pipeline status from load' do
+ project = create(:project, :public)
+ expect(Gitlab::Cache::Ci::ProjectPipelineStatus).not_to receive(:load_in_batch_for_projects)
+
+ get :show, params: { scope: 'projects', search: project.name }
- expect(assigns[:search_objects].count).to eq(0)
+ expect(assigns[:search_objects].first).to eq project
+ end
end
- it "doesn't expose comments on snippets" do
- project = create(:project, :public, :snippets_private)
- note = create(:note_on_project_snippet, project: project)
+ it 'finds issue comments' do
+ project = create(:project, :public)
+ note = create(:note_on_issue, project: project)
get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
- expect(assigns[:search_objects].count).to eq(0)
+ expect(assigns[:search_objects].first).to eq note
end
- end
- context 'with external authorization service enabled' do
- let(:project) { create(:project, namespace: user.namespace) }
- let(:note) { create(:note_on_issue, project: project) }
+ context 'on restricted projects' do
+ context 'when signed out' do
+ before do
+ sign_out(user)
+ end
- before do
- enable_external_authorization_service_check
- end
+ it "doesn't expose comments on issues" do
+ project = create(:project, :public, :issues_private)
+ note = create(:note_on_issue, project: project)
- describe 'GET #show' do
- it 'renders a 403 when no project is given' do
- get :show, params: { scope: 'notes', search: note.note }
+ get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
- expect(response).to have_gitlab_http_status(403)
+ expect(assigns[:search_objects].count).to eq(0)
+ end
end
- it 'renders a 200 when a project was set' do
+ it "doesn't expose comments on merge_requests" do
+ project = create(:project, :public, :merge_requests_private)
+ note = create(:note_on_merge_request, project: project)
+
get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
- expect(response).to have_gitlab_http_status(200)
+ expect(assigns[:search_objects].count).to eq(0)
end
- end
- describe 'GET #autocomplete' do
- it 'renders a 403 when no project is given' do
- get :autocomplete, params: { term: 'hello' }
+ it "doesn't expose comments on snippets" do
+ project = create(:project, :public, :snippets_private)
+ note = create(:note_on_project_snippet, project: project)
- expect(response).to have_gitlab_http_status(403)
+ get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
+
+ expect(assigns[:search_objects].count).to eq(0)
end
+ end
+ end
- it 'renders a 200 when a project was set' do
- get :autocomplete, params: { project_id: project.id, term: 'hello' }
+ describe 'GET #count' do
+ it_behaves_like 'when the user cannot read cross project', :count, { search: 'hello', scope: 'projects' }
+ it_behaves_like 'with external authorization service enabled', :count, { search: 'hello', scope: 'projects' }
- expect(response).to have_gitlab_http_status(200)
- end
+ it 'returns the result count for the given term and scope' do
+ create(:project, :public, name: 'hello world')
+ create(:project, :public, name: 'foo bar')
+
+ get :count, params: { search: 'hello', scope: 'projects' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq({ 'count' => '1' })
+ end
+
+ it 'raises an error if search term is missing' do
+ expect do
+ get :count, params: { scope: 'projects' }
+ end.to raise_error(ActionController::ParameterMissing)
end
+
+ it 'raises an error if search scope is missing' do
+ expect do
+ get :count, params: { search: 'hello' }
+ end.to raise_error(ActionController::ParameterMissing)
+ end
+ end
+
+ describe 'GET #autocomplete' do
+ it_behaves_like 'when the user cannot read cross project', :autocomplete, { term: 'hello' }
+ it_behaves_like 'with external authorization service enabled', :autocomplete, { term: 'hello' }
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 3bf9cdef253..8dab6c71b06 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -16,7 +16,9 @@ FactoryBot.define do
trait(:invited) do
user_id nil
invite_token 'xxx'
- invite_email 'email@email.com'
+ sequence :invite_email do |n|
+ "email#{n}@email.com"
+ end
end
end
end
diff --git a/spec/features/groups/members/filter_members_spec.rb b/spec/features/groups/members/filter_members_spec.rb
index 7aad66ec08f..fc62c92db4e 100644
--- a/spec/features/groups/members/filter_members_spec.rb
+++ b/spec/features/groups/members/filter_members_spec.rb
@@ -19,7 +19,7 @@ describe 'Groups > Members > Filter members' do
expect(first_member).to include(user.name)
expect(second_member).to include(user_with_2fa.name)
- expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Everyone')
+ expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: 'Everyone')
end
it 'shows only 2FA members' do
@@ -27,7 +27,7 @@ describe 'Groups > Members > Filter members' do
expect(first_member).to include(user_with_2fa.name)
expect(members_list.size).to eq(1)
- expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Enabled')
+ expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: 'Enabled')
end
it 'shows only non 2FA members' do
@@ -35,7 +35,7 @@ describe 'Groups > Members > Filter members' do
expect(first_member).to include(user.name)
expect(members_list.size).to eq(1)
- expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: '2FA: Disabled')
+ expect(page).to have_css('.member-filter-2fa-dropdown .dropdown-toggle-text', text: 'Disabled')
end
def visit_members_list(options = {})
diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb
index 779fa74501a..cdd16ae9441 100644
--- a/spec/features/groups/members/manage_members_spec.rb
+++ b/spec/features/groups/members/manage_members_spec.rb
@@ -98,7 +98,9 @@ describe 'Groups > Members > Manage members' do
add_user('test@example.com', 'Reporter')
- page.within(second_row) do
+ click_link('Pending')
+
+ page.within('.content-list.members-list') do
expect(page).to have_content('test@example.com')
expect(page).to have_content('Invited')
expect(page).to have_button('Reporter')
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index 48b0136227e..76709199942 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -19,7 +19,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by access level ascending' do
@@ -27,7 +27,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
it 'sorts by access level descending' do
@@ -35,7 +35,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
it 'sorts by last joined' do
@@ -43,7 +43,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
it 'sorts by oldest joined' do
@@ -51,7 +51,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
it 'sorts by name ascending' do
@@ -59,7 +59,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by name descending' do
@@ -67,7 +67,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
@@ -75,7 +75,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(owner.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
@@ -83,7 +83,7 @@ describe 'Groups > Members > Sort members' do
expect(first_member).to include(developer.name)
expect(second_member).to include(owner.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end
def visit_members_list(sort:)
diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb
index ead564aea28..abae6ffbd71 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -68,12 +68,12 @@ describe 'Merge request > User posts diff notes', :js do
context 'with a match line' do
it 'does not allow commenting on the left side' do
line_holder = find('.match', match: :first).find(:xpath, '..')
- should_not_allow_commenting(line_holder, 'left')
+ match_should_not_allow_commenting(line_holder)
end
it 'does not allow commenting on the right side' do
line_holder = find('.match', match: :first).find(:xpath, '..')
- should_not_allow_commenting(line_holder, 'right')
+ match_should_not_allow_commenting(line_holder)
end
end
@@ -136,7 +136,7 @@ describe 'Merge request > User posts diff notes', :js do
context 'with a match line' do
it 'does not allow commenting' do
- should_not_allow_commenting(find('.match', match: :first))
+ match_should_not_allow_commenting(find('.match', match: :first))
end
end
@@ -222,7 +222,7 @@ describe 'Merge request > User posts diff notes', :js do
context 'with a match line' do
it 'does not allow commenting' do
- should_not_allow_commenting(find('.match', match: :first))
+ match_should_not_allow_commenting(find('.match', match: :first))
end
end
end
@@ -251,6 +251,10 @@ describe 'Merge request > User posts diff notes', :js do
expect(line[:num]).not_to have_css comment_button_class
end
+ def match_should_not_allow_commenting(line_holder)
+ expect(line_holder).not_to have_css comment_button_class
+ end
+
def write_comment_on_line(line_holder, diff_side)
click_diff_line(line_holder, diff_side)
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 6d58d43a295..2d1eb260236 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -17,11 +17,25 @@ describe 'User views diffs', :js do
end
shared_examples 'unfold diffs' do
- it 'unfolds diffs' do
+ it 'unfolds diffs upwards' do
first('.js-unfold').click
-
expect(find('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"] .text-file')).to have_content('.bundle')
end
+
+ it 'unfolds diffs to the start' do
+ first('.js-unfold-all').click
+ expect(find('.file-holder[id="a5cc2925ca8258af241be7e5b0381edf30266302"] .text-file')).to have_content('.rbc')
+ end
+
+ it 'unfolds diffs downwards' do
+ first('.js-unfold-down').click
+ expect(find('.file-holder[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"] .text-file')).to have_content('.popen3')
+ end
+
+ it 'unfolds diffs to the end' do
+ page.all('.js-unfold-down').last
+ expect(find('.file-holder[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9"] .text-file')).to have_content('end')
+ end
end
it 'shows diffs' do
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 88240fbbedc..12f485317d8 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -18,7 +18,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by access level ascending' do
@@ -26,7 +26,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, ascending')
end
it 'sorts by access level descending' do
@@ -34,7 +34,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Access level, descending')
end
it 'sorts by last joined' do
@@ -42,7 +42,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Last joined')
end
it 'sorts by oldest joined' do
@@ -50,7 +50,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest joined')
end
it 'sorts by name ascending' do
@@ -58,7 +58,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, ascending')
end
it 'sorts by name descending' do
@@ -66,7 +66,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Name, descending')
end
it 'sorts by recent sign in', :clean_gitlab_redis_shared_state do
@@ -74,7 +74,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(maintainer.name)
expect(second_member).to include(developer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in')
end
it 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do
@@ -82,7 +82,7 @@ describe 'Projects > Members > Sorting' do
expect(first_member).to include(developer.name)
expect(second_member).to include(maintainer.name)
- expect(page).to have_css('.user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
+ expect(page).to have_css('.qa-user-sort-dropdown .dropdown-toggle-text', text: 'Oldest sign in')
end
def visit_members_list(sort:)
diff --git a/spec/features/search/user_searches_for_users_spec.rb b/spec/features/search/user_searches_for_users_spec.rb
index 2517a843c62..e10c1afc0b8 100644
--- a/spec/features/search/user_searches_for_users_spec.rb
+++ b/spec/features/search/user_searches_for_users_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe 'User searches for users' do
context 'when on the dashboard' do
- it 'finds the user' do
+ it 'finds the user', :js do
create(:user, username: 'gob_bluth', name: 'Gob Bluth')
sign_in(create(:user))
@@ -12,7 +12,7 @@ describe 'User searches for users' do
visit dashboard_projects_path
fill_in 'search', with: 'gob'
- click_button 'Go'
+ find('#search').send_keys(:enter)
expect(page).to have_content('Users 1')
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 c781048d06d..5006631cc14 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -96,6 +96,23 @@ describe 'User uses header search field', :js do
let(:url) { root_path }
let(:scope_name) { 'All GitLab' }
end
+
+ context 'when searching through the search field' do
+ before do
+ create(:issue, project: project, title: 'project issue')
+
+ fill_in('search', with: 'project')
+ find('#search').send_keys(:enter)
+ end
+
+ it 'displays result counts for all categories' do
+ expect(page).to have_content('Projects 1')
+ expect(page).to have_content('Issues 1')
+ expect(page).to have_content('Merge requests 0')
+ expect(page).to have_content('Milestones 0')
+ expect(page).to have_content('Users 0')
+ end
+ end
end
context 'when user is in a project scope' do
diff --git a/spec/frontend/notes/components/discussion_keyboard_navigator_spec.js b/spec/frontend/notes/components/discussion_keyboard_navigator_spec.js
new file mode 100644
index 00000000000..6d50713999d
--- /dev/null
+++ b/spec/frontend/notes/components/discussion_keyboard_navigator_spec.js
@@ -0,0 +1,77 @@
+/* global Mousetrap */
+import 'mousetrap';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import DiscussionKeyboardNavigator from '~/notes/components/discussion_keyboard_navigator.vue';
+import notesModule from '~/notes/stores/modules';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+const NEXT_ID = 'abc123';
+const PREV_ID = 'def456';
+const NEXT_DIFF_ID = 'abc123_diff';
+const PREV_DIFF_ID = 'def456_diff';
+
+describe('notes/components/discussion_keyboard_navigator', () => {
+ let storeOptions;
+ let wrapper;
+ let store;
+
+ const createComponent = (options = {}) => {
+ store = new Vuex.Store(storeOptions);
+
+ wrapper = shallowMount(DiscussionKeyboardNavigator, {
+ localVue,
+ store,
+ ...options,
+ });
+
+ wrapper.vm.jumpToDiscussion = jest.fn();
+ };
+
+ beforeEach(() => {
+ const notes = notesModule();
+
+ notes.getters.nextUnresolvedDiscussionId = () => (currId, isDiff) =>
+ isDiff ? NEXT_DIFF_ID : NEXT_ID;
+ notes.getters.previousUnresolvedDiscussionId = () => (currId, isDiff) =>
+ isDiff ? PREV_DIFF_ID : PREV_ID;
+
+ storeOptions = {
+ modules: {
+ notes,
+ },
+ };
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ storeOptions = null;
+ store = null;
+ });
+
+ describe.each`
+ isDiffView | expectedNextId | expectedPrevId
+ ${true} | ${NEXT_DIFF_ID} | ${PREV_DIFF_ID}
+ ${false} | ${NEXT_ID} | ${PREV_ID}
+ `('when isDiffView is $isDiffView', ({ isDiffView, expectedNextId, expectedPrevId }) => {
+ beforeEach(() => {
+ createComponent({ propsData: { isDiffView } });
+ });
+
+ it('calls jumpToNextDiscussion when pressing `n`', () => {
+ Mousetrap.trigger('n');
+
+ expect(wrapper.vm.jumpToDiscussion).toHaveBeenCalledWith(expectedNextId);
+ expect(wrapper.vm.currentDiscussionId).toEqual(expectedNextId);
+ });
+
+ it('calls jumpToPreviousDiscussion when pressing `p`', () => {
+ Mousetrap.trigger('p');
+
+ expect(wrapper.vm.jumpToDiscussion).toHaveBeenCalledWith(expectedPrevId);
+ expect(wrapper.vm.currentDiscussionId).toEqual(expectedPrevId);
+ });
+ });
+});
diff --git a/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap b/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap
new file mode 100644
index 00000000000..ce456d6c899
--- /dev/null
+++ b/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`pages/search/show/refresh_counts fetches and displays search counts 1`] = `
+"<div class=\\"badge\\">22</div>
+<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=issues\\">4</div>
+<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=merge_requests\\">5</div>"
+`;
diff --git a/spec/frontend/pages/search/show/refresh_counts_spec.js b/spec/frontend/pages/search/show/refresh_counts_spec.js
new file mode 100644
index 00000000000..ead268b3971
--- /dev/null
+++ b/spec/frontend/pages/search/show/refresh_counts_spec.js
@@ -0,0 +1,35 @@
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'helpers/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import refreshCounts from '~/pages/search/show/refresh_counts';
+
+const URL = `${TEST_HOST}/search/count?search=lorem+ipsum&project_id=3`;
+const urlWithScope = scope => `${URL}&scope=${scope}`;
+const counts = [{ scope: 'issues', count: 4 }, { scope: 'merge_requests', count: 5 }];
+const fixture = `<div class="badge">22</div>
+<div class="badge js-search-count hidden" data-url="${urlWithScope('issues')}"></div>
+<div class="badge js-search-count hidden" data-url="${urlWithScope('merge_requests')}"></div>`;
+
+describe('pages/search/show/refresh_counts', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ setFixtures(fixture);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('fetches and displays search counts', () => {
+ counts.forEach(({ scope, count }) => {
+ mock.onGet(urlWithScope(scope)).reply(200, { count });
+ });
+
+ // assert before act behavior
+ return refreshCounts().then(() => {
+ expect(document.body.innerHTML).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index c69493b579f..2ab72679ee7 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -177,4 +177,48 @@ describe SearchHelper do
end
end
end
+
+ describe 'search_filter_link' do
+ it 'renders a search filter link for the current scope' do
+ @scope = 'projects'
+ @search_results = double
+
+ expect(@search_results).to receive(:formatted_count).with('projects').and_return('23')
+
+ link = search_filter_link('projects', 'Projects')
+
+ expect(link).to have_css('li.active')
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
+ expect(link).to have_css('span.badge.badge-pill:not(.js-search-count):not(.hidden):not([data-url])', text: '23')
+ end
+
+ it 'renders a search filter link for another scope' do
+ link = search_filter_link('projects', 'Projects')
+ count_path = search_count_path(scope: 'projects')
+
+ expect(link).to have_css('li:not([class="active"])')
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
+ expect(link).to have_css("span.badge.badge-pill.js-search-count.hidden[data-url='#{count_path}']", text: '')
+ end
+
+ it 'merges in the current search params and given params' do
+ expect(self).to receive(:params).and_return(
+ ActionController::Parameters.new(
+ search: 'hello',
+ scope: 'ignored',
+ other_param: 'ignored'
+ )
+ )
+
+ link = search_filter_link('projects', 'Projects', search: { project_id: 23 })
+
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects', search: 'hello', project_id: 23))
+ end
+
+ it 'assigns given data attributes on the list container' do
+ link = search_filter_link('projects', 'Projects', data: { foo: 'bar' })
+
+ expect(link).to have_css('li[data-foo="bar"]')
+ end
+ end
end
diff --git a/spec/javascripts/diffs/components/compare_versions_spec.js b/spec/javascripts/diffs/components/compare_versions_spec.js
index 77f8352047c..ef4bb470734 100644
--- a/spec/javascripts/diffs/components/compare_versions_spec.js
+++ b/spec/javascripts/diffs/components/compare_versions_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import CompareVersionsComponent from '~/diffs/components/compare_versions.vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffsMockData from '../mock_data/merge_request_diffs';
import getDiffWithCommit from '../mock_data/diff_with_commit';
@@ -10,6 +10,8 @@ describe('CompareVersions', () => {
const targetBranch = { branchName: 'tmp-wine-dev', versionIndex: -1 };
beforeEach(() => {
+ const store = createStore();
+
store.state.diffs.addedLines = 10;
store.state.diffs.removedLines = 20;
store.state.diffs.diffFiles.push('test');
diff --git a/spec/javascripts/diffs/components/diff_expansion_cell_spec.js b/spec/javascripts/diffs/components/diff_expansion_cell_spec.js
new file mode 100644
index 00000000000..63c50c09fce
--- /dev/null
+++ b/spec/javascripts/diffs/components/diff_expansion_cell_spec.js
@@ -0,0 +1,64 @@
+import Vue from 'vue';
+import { createStore } from '~/mr_notes/stores';
+import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+
+const EXPAND_UP_CLASS = '.js-unfold';
+const EXPAND_DOWN_CLASS = '.js-unfold-down';
+const EXPAND_ALL_CLASS = '.js-unfold-all';
+
+describe('DiffExpansionCell', () => {
+ const matchLine = diffFileMockData.highlighted_diff_lines[5];
+
+ const createComponent = (options = {}) => {
+ const cmp = Vue.extend(DiffExpansionCell);
+ const defaults = {
+ fileHash: diffFileMockData.file_hash,
+ contextLinesPath: 'contextLinesPath',
+ line: matchLine,
+ isTop: false,
+ isBottom: false,
+ };
+ const props = Object.assign({}, defaults, options);
+
+ return createComponentWithStore(cmp, createStore(), props).$mount();
+ };
+
+ describe('top row', () => {
+ it('should have "expand up" and "show all" option', () => {
+ const vm = createComponent({
+ isTop: true,
+ });
+ const el = vm.$el;
+
+ expect(el.querySelector(EXPAND_UP_CLASS)).not.toBe(null);
+ expect(el.querySelector(EXPAND_DOWN_CLASS)).toBe(null);
+ expect(el.querySelector(EXPAND_ALL_CLASS)).not.toBe(null);
+ });
+ });
+
+ describe('middle row', () => {
+ it('should have "expand down", "show all", "expand up" option', () => {
+ const vm = createComponent();
+ const el = vm.$el;
+
+ expect(el.querySelector(EXPAND_UP_CLASS)).not.toBe(null);
+ expect(el.querySelector(EXPAND_DOWN_CLASS)).not.toBe(null);
+ expect(el.querySelector(EXPAND_ALL_CLASS)).not.toBe(null);
+ });
+ });
+
+ describe('bottom row', () => {
+ it('should have "expand down" and "show all" option', () => {
+ const vm = createComponent({
+ isBottom: true,
+ });
+ const el = vm.$el;
+
+ expect(el.querySelector(EXPAND_UP_CLASS)).toBe(null);
+ expect(el.querySelector(EXPAND_DOWN_CLASS)).not.toBe(null);
+ expect(el.querySelector(EXPAND_ALL_CLASS)).not.toBe(null);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index ef4589ada48..3ca2d1dc934 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import DiffFileComponent from '~/diffs/components/diff_file.vue';
import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
-import store from 'ee_else_ce/mr_notes/stores';
+import { createStore } from 'ee_else_ce/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -9,14 +9,18 @@ describe('DiffFile', () => {
let vm;
beforeEach(() => {
- vm = createComponentWithStore(Vue.extend(DiffFileComponent), store, {
+ vm = createComponentWithStore(Vue.extend(DiffFileComponent), createStore(), {
file: JSON.parse(JSON.stringify(diffFileMockData)),
canCurrentUserFork: false,
}).$mount();
});
+ afterEach(() => {
+ vm.$destroy();
+ });
+
describe('template', () => {
- it('should render component with file header, file content components', () => {
+ it('should render component with file header, file content components', done => {
const el = vm.$el;
const { file_hash, file_path } = vm.file;
@@ -30,9 +34,13 @@ describe('DiffFile', () => {
vm.file.renderIt = true;
- vm.$nextTick(() => {
- expect(el.querySelectorAll('.line_content').length).toBeGreaterThan(5);
- });
+ vm.$nextTick()
+ .then(() => {
+ expect(el.querySelectorAll('.line_content').length).toBe(5);
+ expect(el.querySelectorAll('.js-line-expansion-content').length).toBe(1);
+ })
+ .then(done)
+ .catch(done.fail);
});
describe('collapsed', () => {
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index 038db8eaa7c..6bb704658fb 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import DiffLineGutterContent from '~/diffs/components/diff_line_gutter_content.vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import discussionsMockData from '../mock_data/diff_discussions';
import diffFileMockData from '../mock_data/diff_file';
@@ -23,7 +23,7 @@ describe('DiffLineGutterContent', () => {
props.fileHash = getDiffFileMock().file_hash;
props.contextLinesPath = '/context/lines/path';
- return createComponentWithStore(cmp, store, props).$mount();
+ return createComponentWithStore(cmp, createStore(), props).$mount();
};
describe('computed', () => {
@@ -61,7 +61,7 @@ describe('DiffLineGutterContent', () => {
contextLinesPath: '/context/lines/path',
};
props.line.discussions = [Object.assign({}, discussionsMockData)];
- const component = createComponentWithStore(cmp, store, props).$mount();
+ const component = createComponentWithStore(cmp, createStore(), props).$mount();
expect(component.hasDiscussions).toEqual(true);
expect(component.shouldShowAvatarsOnGutter).toEqual(true);
@@ -70,15 +70,6 @@ describe('DiffLineGutterContent', () => {
});
describe('template', () => {
- it('should render three dots for context lines', () => {
- const component = createComponent({
- isMatchLine: true,
- });
-
- expect(component.$el.querySelector('span').classList.contains('context-cell')).toEqual(true);
- expect(component.$el.innerText).toEqual('...');
- });
-
it('should render comment button', () => {
const component = createComponent({
showCommentButton: true,
diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
index b983dc35a57..237cfccfa29 100644
--- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import DiffLineNoteForm from '~/diffs/components/diff_line_note_form.vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
import { noteableDataMock } from '../../notes/mock_data';
@@ -15,7 +15,7 @@ describe('DiffLineNoteForm', () => {
diffFile = getDiffFileMock();
diffLines = diffFile.highlighted_diff_lines;
- component = createComponentWithStore(Vue.extend(DiffLineNoteForm), store, {
+ component = createComponentWithStore(Vue.extend(DiffLineNoteForm), createStore(), {
diffFileHash: diffFile.file_hash,
diffLines,
line: diffLines[0],
diff --git a/spec/javascripts/diffs/components/diff_table_cell_spec.js b/spec/javascripts/diffs/components/diff_table_cell_spec.js
index 170e661beea..a5a042c577c 100644
--- a/spec/javascripts/diffs/components/diff_table_cell_spec.js
+++ b/spec/javascripts/diffs/components/diff_table_cell_spec.js
@@ -1,12 +1,12 @@
import Vue from 'vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import DiffTableCell from '~/diffs/components/diff_table_cell.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
describe('DiffTableCell', () => {
const createComponent = options =>
- createComponentWithStore(Vue.extend(DiffTableCell), store, {
+ createComponentWithStore(Vue.extend(DiffTableCell), createStore(), {
line: diffFileMockData.highlighted_diff_lines[0],
fileHash: diffFileMockData.file_hash,
contextLinesPath: 'contextLinesPath',
diff --git a/spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js b/spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js
new file mode 100644
index 00000000000..290b3d7c803
--- /dev/null
+++ b/spec/javascripts/diffs/components/inline_diff_expansion_row_spec.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import { createStore } from '~/mr_notes/stores';
+import InlineDiffExpansionRow from '~/diffs/components/inline_diff_expansion_row.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('InlineDiffExpansionRow', () => {
+ const matchLine = diffFileMockData.highlighted_diff_lines[5];
+
+ const createComponent = (options = {}) => {
+ const cmp = Vue.extend(InlineDiffExpansionRow);
+ const defaults = {
+ fileHash: diffFileMockData.file_hash,
+ contextLinesPath: 'contextLinesPath',
+ line: matchLine,
+ isTop: false,
+ isBottom: false,
+ };
+ const props = Object.assign({}, defaults, options);
+
+ return createComponentWithStore(cmp, createStore(), props).$mount();
+ };
+
+ describe('template', () => {
+ it('should render expansion row for match lines', () => {
+ const vm = createComponent();
+
+ expect(vm.$el.classList.contains('line_expansion')).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/inline_diff_table_row_spec.js b/spec/javascripts/diffs/components/inline_diff_table_row_spec.js
index 97926f6625e..0ddffe926d9 100644
--- a/spec/javascripts/diffs/components/inline_diff_table_row_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_table_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import store from '~/mr_notes/stores';
+import { createStore } from '~/mr_notes/stores';
import InlineDiffTableRow from '~/diffs/components/inline_diff_table_row.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -9,7 +9,7 @@ describe('InlineDiffTableRow', () => {
const thisLine = diffFileMockData.highlighted_diff_lines[0];
beforeEach(() => {
- vm = createComponentWithStore(Vue.extend(InlineDiffTableRow), store, {
+ vm = createComponentWithStore(Vue.extend(InlineDiffTableRow), createStore(), {
line: thisLine,
fileHash: diffFileMockData.file_hash,
contextLinesPath: 'contextLinesPath',
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index 9b61dbe7975..486d9629e26 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
-import store from 'ee_else_ce/mr_notes/stores';
+import { createStore } from 'ee_else_ce/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions';
@@ -15,6 +15,8 @@ describe('InlineDiffView', () => {
beforeEach(done => {
const diffFile = getDiffFileMock();
+ const store = createStore();
+
store.dispatch('diffs/setInlineDiffViewType');
component = createComponentWithStore(Vue.extend(InlineDiffView), store, {
diffFile,
@@ -28,9 +30,9 @@ describe('InlineDiffView', () => {
it('should have rendered diff lines', () => {
const el = component.$el;
- expect(el.querySelectorAll('tr.line_holder').length).toEqual(6);
+ expect(el.querySelectorAll('tr.line_holder').length).toEqual(5);
expect(el.querySelectorAll('tr.line_holder.new').length).toEqual(2);
- expect(el.querySelectorAll('tr.line_holder.match').length).toEqual(1);
+ expect(el.querySelectorAll('tr.line_expansion.match').length).toEqual(1);
expect(el.textContent.indexOf('Bad dates')).toBeGreaterThan(-1);
});
diff --git a/spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js b/spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js
new file mode 100644
index 00000000000..a766ebb5efb
--- /dev/null
+++ b/spec/javascripts/diffs/components/parallel_diff_expansion_row_spec.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import { createStore } from '~/mr_notes/stores';
+import ParallelDiffExpansionRow from '~/diffs/components/parallel_diff_expansion_row.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import diffFileMockData from '../mock_data/diff_file';
+
+describe('ParallelDiffExpansionRow', () => {
+ const matchLine = diffFileMockData.highlighted_diff_lines[5];
+
+ const createComponent = (options = {}) => {
+ const cmp = Vue.extend(ParallelDiffExpansionRow);
+ const defaults = {
+ fileHash: diffFileMockData.file_hash,
+ contextLinesPath: 'contextLinesPath',
+ line: matchLine,
+ isTop: false,
+ isBottom: false,
+ };
+ const props = Object.assign({}, defaults, options);
+
+ return createComponentWithStore(cmp, createStore(), props).$mount();
+ };
+
+ describe('template', () => {
+ it('should render expansion row for match lines', () => {
+ const vm = createComponent();
+
+ expect(vm.$el.classList.contains('line_expansion')).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
index 236bda96145..191313bf487 100644
--- a/spec/javascripts/diffs/components/parallel_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
-import store from 'ee_else_ce/mr_notes/stores';
+import { createStore } from 'ee_else_ce/mr_notes/stores';
import * as constants from '~/diffs/constants';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -12,7 +12,7 @@ describe('ParallelDiffView', () => {
beforeEach(() => {
const diffFile = getDiffFileMock();
- component = createComponentWithStore(Vue.extend(ParallelDiffView), store, {
+ component = createComponentWithStore(Vue.extend(ParallelDiffView), createStore(), {
diffFile,
diffLines: diffFile.parallel_diff_lines,
}).$mount();
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index f8872a3eb13..5806cb47034 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -380,7 +380,9 @@ describe('DiffsStoreActions', () => {
const params = { since: 6, to: 26 };
const lineNumbers = { oldLineNumber: 3, newLineNumber: 5 };
const fileHash = 'ff9200';
- const options = { endpoint, params, lineNumbers, fileHash };
+ const isExpandDown = false;
+ const nextLineNumbers = {};
+ const options = { endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers };
const mock = new MockAdapter(axios);
const contextLines = { contextLines: [{ lineCode: 6 }] };
mock.onGet(endpoint).reply(200, contextLines);
@@ -392,7 +394,7 @@ describe('DiffsStoreActions', () => {
[
{
type: types.ADD_CONTEXT_LINES,
- payload: { lineNumbers, contextLines, params, fileHash },
+ payload: { lineNumbers, contextLines, params, fileHash, isExpandDown, nextLineNumbers },
},
],
[],
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 9c13c7ceb7a..3e033b6c9dc 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -81,6 +81,8 @@ describe('DiffsStoreMutations', () => {
params: {
bottom: true,
},
+ isExpandDown: false,
+ nextLineNumbers: {},
};
const diffFile = {
file_hash: options.fileHash,
@@ -108,6 +110,8 @@ describe('DiffsStoreMutations', () => {
options.contextLines,
options.lineNumbers,
options.params.bottom,
+ options.isExpandDown,
+ options.nextLineNumbers,
);
expect(addContextLinesSpy).toHaveBeenCalledWith({
@@ -116,6 +120,7 @@ describe('DiffsStoreMutations', () => {
contextLines: options.contextLines,
bottom: options.params.bottom,
lineNumbers: options.lineNumbers,
+ isExpandDown: false,
});
});
});
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index 1f877910125..65eb4c9d2a3 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -260,6 +260,17 @@ describe('DiffsStoreUtils', () => {
expect(linesWithReferences[1].meta_data.old_pos).toEqual(2);
expect(linesWithReferences[1].meta_data.new_pos).toEqual(3);
});
+
+ it('should add correct line references when isExpandDown is true', () => {
+ const lines = [{ type: null }, { type: MATCH_LINE_TYPE }];
+ const linesWithReferences = utils.addLineReferences(lines, lineNumbers, false, true, {
+ old_line: 10,
+ new_line: 11,
+ });
+
+ expect(linesWithReferences[1].meta_data.old_pos).toEqual(10);
+ expect(linesWithReferences[1].meta_data.new_pos).toEqual(11);
+ });
});
describe('trimFirstCharOfLineContent', () => {
diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js
index b0f4ab2b12d..a111333ac80 100644
--- a/spec/javascripts/issue_show/components/form_spec.js
+++ b/spec/javascripts/issue_show/components/form_spec.js
@@ -1,81 +1,98 @@
import Vue from 'vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import formComponent from '~/issue_show/components/form.vue';
import eventHub from '~/issue_show/event_hub';
describe('Inline edit form component', () => {
let vm;
- let autosave;
- let autosaveObj;
-
- beforeEach(done => {
- autosaveObj = { reset: jasmine.createSpy() };
-
- autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
+ const defaultProps = {
+ canDestroy: true,
+ formState: {
+ title: 'b',
+ description: 'a',
+ lockedWarningVisible: false,
+ },
+ issuableType: 'issue',
+ markdownPreviewPath: '/',
+ markdownDocsPath: '/',
+ projectPath: '/',
+ projectNamespace: '/',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+ const createComponent = props => {
const Component = Vue.extend(formComponent);
- vm = new Component({
- propsData: {
- canDestroy: true,
- formState: {
- title: 'b',
- description: 'a',
- lockedWarningVisible: false,
- },
- issuableType: 'issue',
- markdownPreviewPath: '/',
- markdownDocsPath: '/',
- projectPath: '/',
- projectNamespace: '/',
- },
- }).$mount();
-
- Vue.nextTick(done);
- });
+ vm = mountComponent(Component, {
+ ...defaultProps,
+ ...props,
+ });
+ };
it('does not render template selector if no templates exist', () => {
+ createComponent();
+
expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull();
});
- it('renders template selector when templates exists', done => {
- vm.issuableTemplates = ['test'];
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
+ it('renders template selector when templates exists', () => {
+ createComponent({ issuableTemplates: ['test'] });
- done();
- });
+ expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull();
});
it('hides locked warning by default', () => {
+ createComponent();
+
expect(vm.$el.querySelector('.alert')).toBeNull();
});
- it('shows locked warning if formState is different', done => {
- vm.formState.lockedWarningVisible = true;
+ it('shows locked warning if formState is different', () => {
+ createComponent({ formState: { ...defaultProps.formState, lockedWarningVisible: true } });
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.alert')).not.toBeNull();
+ expect(vm.$el.querySelector('.alert')).not.toBeNull();
+ });
- done();
+ it('hides locked warning when currently saving', () => {
+ createComponent({
+ formState: { ...defaultProps.formState, updateLoading: true, lockedWarningVisible: true },
});
- });
- it('initialized Autosave on mount', () => {
- expect(autosave).toHaveBeenCalledTimes(2);
+ expect(vm.$el.querySelector('.alert')).toBeNull();
});
- it('calls reset on autosave when eventHub emits appropriate events', () => {
- eventHub.$emit('close.form');
+ describe('autosave', () => {
+ let autosaveObj;
+ let autosave;
+
+ beforeEach(() => {
+ autosaveObj = { reset: jasmine.createSpy() };
+ autosave = spyOnDependency(formComponent, 'Autosave').and.returnValue(autosaveObj);
+ });
+
+ it('initialized Autosave on mount', () => {
+ createComponent();
- expect(autosaveObj.reset).toHaveBeenCalledTimes(2);
+ expect(autosave).toHaveBeenCalledTimes(2);
+ });
+
+ it('calls reset on autosave when eventHub emits appropriate events', () => {
+ createComponent();
+
+ eventHub.$emit('close.form');
- eventHub.$emit('delete.issuable');
+ expect(autosaveObj.reset).toHaveBeenCalledTimes(2);
- expect(autosaveObj.reset).toHaveBeenCalledTimes(4);
+ eventHub.$emit('delete.issuable');
- eventHub.$emit('update.issuable');
+ expect(autosaveObj.reset).toHaveBeenCalledTimes(4);
- expect(autosaveObj.reset).toHaveBeenCalledTimes(6);
+ eventHub.$emit('update.issuable');
+
+ expect(autosaveObj.reset).toHaveBeenCalledTimes(6);
+ });
});
});
diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js
index 3485eec7763..24bb6b9a48b 100644
--- a/spec/javascripts/jobs/components/job_log_spec.js
+++ b/spec/javascripts/jobs/components/job_log_spec.js
@@ -11,7 +11,7 @@ describe('Job Log', () => {
let vm;
const trace =
- 'Running with gitlab-runner 11.1.0 (081978aa)<br> on docker-auto-scale-com d5ae8d25<br>Using Docker executor with image dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29 ...<br>';
+ '<span>Running with gitlab-runner 12.1.0 (de7731dd)<br/></span><span> on docker-auto-scale-com d5ae8d25<br/></span><div class="js-section-start fa fa-caret-down append-right-8 cursor-pointer" data-timestamp="1565502765" data-section="prepare-executor" role="button"></div><span class="section js-section-header section-header js-s-prepare-executor">Using Docker executor with image ruby:2.6 ...<br/></span>';
beforeEach(() => {
store = createStore();
@@ -32,7 +32,7 @@ describe('Job Log', () => {
});
expect(vm.$el.querySelector('code').textContent).toContain(
- 'Running with gitlab-runner 11.1.0 (081978aa)',
+ 'Running with gitlab-runner 12.1.0 (de7731dd)',
);
});
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index c3ed079e33b..71dcba114a9 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -256,6 +256,54 @@ describe('Getters Notes Store', () => {
});
});
+ describe('previousUnresolvedDiscussionId', () => {
+ describe('with unresolved discussions', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'],
+ };
+
+ it('with bogus returns falsey', () => {
+ expect(getters.previousUnresolvedDiscussionId(state, localGetters)('bogus')).toBe('456');
+ });
+
+ [
+ { id: '123', expected: '789' },
+ { id: '456', expected: '123' },
+ { id: '789', expected: '456' },
+ ].forEach(({ id, expected }) => {
+ it(`with ${id}, returns previous value`, () => {
+ expect(getters.previousUnresolvedDiscussionId(state, localGetters)(id)).toBe(expected);
+ });
+ });
+ });
+
+ describe('with 1 unresolved discussion', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsOrdered: () => ['123'],
+ };
+
+ it('with bogus returns id', () => {
+ expect(getters.previousUnresolvedDiscussionId(state, localGetters)('bogus')).toBe('123');
+ });
+
+ it('with match, returns value', () => {
+ expect(getters.previousUnresolvedDiscussionId(state, localGetters)('123')).toEqual('123');
+ });
+ });
+
+ describe('with 0 unresolved discussions', () => {
+ const localGetters = {
+ unresolvedDiscussionsIdsOrdered: () => [],
+ };
+
+ it('returns undefined', () => {
+ expect(
+ getters.previousUnresolvedDiscussionId(state, localGetters)('bogus'),
+ ).toBeUndefined();
+ });
+ });
+ });
+
describe('firstUnresolvedDiscussionId', () => {
const localGetters = {
unresolvedDiscussionsIdsByDate: ['123', '456'],
diff --git a/spec/lib/bitbucket/representation/comment_spec.rb b/spec/lib/bitbucket/representation/comment_spec.rb
index 2dcc933ee61..1874296df8c 100644
--- a/spec/lib/bitbucket/representation/comment_spec.rb
+++ b/spec/lib/bitbucket/representation/comment_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe Bitbucket::Representation::Comment do
describe '#author' do
- it { expect(described_class.new('user' => { 'username' => 'Ben' }).author).to eq('Ben') }
+ it { expect(described_class.new('user' => { 'nickname' => 'Ben' }).author).to eq('Ben') }
it { expect(described_class.new({}).author).to be_nil }
end
diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb
index c7d1ebdd597..655b9b78b47 100644
--- a/spec/lib/bitbucket/representation/issue_spec.rb
+++ b/spec/lib/bitbucket/representation/issue_spec.rb
@@ -17,7 +17,7 @@ describe Bitbucket::Representation::Issue do
end
describe '#author' do
- it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') }
+ it { expect(described_class.new({ 'reporter' => { 'nickname' => 'Ben' } }).author).to eq('Ben') }
it { expect(described_class.new({}).author).to be_nil }
end
diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb
index 7cf43b2b6fd..70b51b8efec 100644
--- a/spec/lib/bitbucket/representation/pull_request_spec.rb
+++ b/spec/lib/bitbucket/representation/pull_request_spec.rb
@@ -8,7 +8,7 @@ describe Bitbucket::Representation::PullRequest do
end
describe '#author' do
- it { expect(described_class.new({ 'author' => { 'username' => 'Ben' } }).author).to eq('Ben') }
+ it { expect(described_class.new({ 'author' => { 'nickname' => 'Ben' } }).author).to eq('Ben') }
it { expect(described_class.new({}).author).to be_nil }
end
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
index 988710b7c4d..aa0217856ee 100644
--- a/spec/lib/bitbucket_server/client_spec.rb
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -58,6 +58,17 @@ describe BitbucketServer::Client do
subject.repos(page_offset: 10, limit: 25)
end
+
+ context 'when filter param is passed' do
+ let(:filter) { 'test' }
+ let(:expected_path) { "#{path}?name=#{filter}" }
+
+ it 'requests a collection with filter applied' do
+ expect(BitbucketServer::Paginator).to receive(:new).with(anything, expected_path, :repo, page_offset: 0, limit: nil)
+
+ subject.repos(filter: filter)
+ end
+ end
end
describe '#create_branch' do
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 280941ff601..3d0d3f91859 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -25,12 +25,12 @@ describe Gitlab::BitbucketImport::Importer do
let(:reporters) do
[
nil,
- { "username" => "reporter1" },
+ { "nickname" => "reporter1" },
nil,
- { "username" => "reporter2" },
- { "username" => "reporter1" },
+ { "nickname" => "reporter2" },
+ { "nickname" => "reporter1" },
nil,
- { "username" => "reporter3" }
+ { "nickname" => "reporter3" }
]
end
@@ -115,6 +115,7 @@ describe Gitlab::BitbucketImport::Importer do
created_at: Time.now,
updated_at: Time.now)
end
+ let(:author_line) { "*Created by: someuser*\n\n" }
before do
allow(subject).to receive(:import_wiki)
@@ -128,7 +129,7 @@ describe Gitlab::BitbucketImport::Importer do
old_pos: nil,
new_pos: 4,
note: 'Hello world',
- author: 'root',
+ author: 'someuser',
created_at: Time.now,
updated_at: Time.now,
inline?: true,
@@ -139,7 +140,7 @@ describe Gitlab::BitbucketImport::Importer do
iid: 3,
file_path: '.gitmodules',
note: 'Hello world',
- author: 'root',
+ author: 'someuser',
created_at: Time.now,
updated_at: Time.now,
inline?: true,
@@ -163,11 +164,33 @@ describe Gitlab::BitbucketImport::Importer do
notes = merge_request.notes.order(:id).to_a
start_note = notes.first
expect(start_note).to be_a(DiffNote)
- expect(start_note.note).to eq(@inline_note.note)
+ expect(start_note.note).to include(@inline_note.note)
+ expect(start_note.note).to include(author_line)
reply_note = notes.last
expect(reply_note).to be_a(DiffNote)
- expect(reply_note.note).to eq(@reply.note)
+ expect(reply_note.note).to include(@reply.note)
+ expect(reply_note.note).to include(author_line)
+ end
+
+ context 'when user exists in GitLab' do
+ let!(:existing_user) { create(:user, username: 'someuser') }
+ let!(:identity) { create(:identity, provider: 'bitbucket', extern_uid: existing_user.username, user: existing_user) }
+
+ it 'does not add author line to comments' do
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request = MergeRequest.first
+
+ notes = merge_request.notes.order(:id).to_a
+ start_note = notes.first
+ expect(start_note.note).to eq(@inline_note.note)
+ expect(start_note.note).not_to include(author_line)
+
+ reply_note = notes.last
+ expect(reply_note.note).to eq(@reply.note)
+ expect(reply_note.note).not_to include(author_line)
+ end
end
context 'when importing a pull request throws an exception' do
diff --git a/spec/lib/gitlab/ci/ansi2html_spec.rb b/spec/lib/gitlab/ci/ansi2html_spec.rb
index b6b3de4bc4a..c8afcbd053d 100644
--- a/spec/lib/gitlab/ci/ansi2html_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2html_spec.rb
@@ -209,7 +209,7 @@ describe Gitlab::Ci::Ansi2html do
let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
let(:section_start_html) do
- '<div class="js-section-start fa fa-caret-down pr-2 cursor-pointer"' \
+ '<div class="js-section-start section-start fa fa-caret-down pr-2 cursor-pointer"' \
" data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{class_name(section_name)}\"" \
' role="button"></div>'
end
diff --git a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
index 180520b27e7..6ed9dda08d7 100644
--- a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
+++ b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
@@ -17,9 +17,7 @@ describe Gitlab::Cluster::PumaWorkerKillerObserver do
it 'increments timeout counter' do
worker = double(index: 0)
- expect(counter)
- .to receive(:increment)
- .with({ worker: 'worker_0' })
+ expect(counter).to receive(:increment)
subject.callback.call(worker)
end
diff --git a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb b/spec/lib/gitlab/gitaly_client/notification_service_spec.rb
deleted file mode 100644
index ffc3a09be30..00000000000
--- a/spec/lib/gitlab/gitaly_client/notification_service_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::GitalyClient::NotificationService do
- describe '#post_receive' do
- let(:project) { create(:project) }
- let(:storage_name) { project.repository_storage }
- let(:relative_path) { project.disk_path + '.git' }
- subject { described_class.new(project.repository) }
-
- it 'sends a post_receive message' do
- expect_any_instance_of(Gitaly::NotificationService::Stub)
- .to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
-
- subject.post_receive
- end
- end
-end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 6d70b147666..a211675bbf2 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2450,7 +2450,21 @@
"author": {
"name": "Ottis Schuster II"
},
- "events": []
+ "events": [],
+ "suggestions": [
+ {
+ "id": 1,
+ "note_id": 674,
+ "relative_order": 0,
+ "applied": false,
+ "commit_id": null,
+ "from_content": "Original line\n",
+ "to_content": "New line\n",
+ "lines_above": 0,
+ "lines_below": 0,
+ "outdated": false
+ }
+ ]
},
{
"id": 675,
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index baec24590b4..d6e1fbaa979 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -125,6 +125,13 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty
end
+ it 'restores suggestion' do
+ note = Note.find_by("note LIKE 'Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum%'")
+
+ expect(note.suggestions.count).to eq(1)
+ expect(note.suggestions.first.from_content).to eq("Original line\n")
+ end
+
context 'event at forth level of the tree' do
let(:event) { Event.where(action: 6).first }
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 4a41d5cf51e..c7462500c82 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -22,6 +22,28 @@ describe Gitlab::ProjectSearchResults do
it { expect(results.query).to eq('hello world') }
end
+ describe '#formatted_count' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:results) { described_class.new(user, project, query) }
+
+ where(:scope, :count_method, :expected) do
+ 'blobs' | :blobs_count | '1234'
+ 'notes' | :limited_notes_count | '1000+'
+ 'wiki_blobs' | :wiki_blobs_count | '1234'
+ 'commits' | :commits_count | '1234'
+ 'projects' | :limited_projects_count | '1000+'
+ 'unknown' | nil | nil
+ end
+
+ with_them do
+ it 'returns the expected formatted count' do
+ expect(results).to receive(count_method).and_return(1234) if count_method
+ expect(results.formatted_count(scope)).to eq(expected)
+ end
+ end
+ end
+
shared_examples 'general blob search' do |entity_type, blob_kind|
let(:query) { 'files' }
subject(:results) { described_class.new(user, project, query).objects(blob_type) }
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index 3d27156b356..c287da19343 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -29,6 +29,43 @@ describe Gitlab::SearchResults do
end
end
+ describe '#formatted_count' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:scope, :count_method, :expected) do
+ 'projects' | :limited_projects_count | '1000+'
+ 'issues' | :limited_issues_count | '1000+'
+ 'merge_requests' | :limited_merge_requests_count | '1000+'
+ 'milestones' | :limited_milestones_count | '1000+'
+ 'users' | :limited_users_count | '1000+'
+ 'unknown' | nil | nil
+ end
+
+ with_them do
+ it 'returns the expected formatted count' do
+ expect(results).to receive(count_method).and_return(1234) if count_method
+ expect(results.formatted_count(scope)).to eq(expected)
+ end
+ end
+ end
+
+ describe '#formatted_limited_count' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:count, :expected) do
+ 23 | '23'
+ 1000 | '1000'
+ 1001 | '1000+'
+ 1234 | '1000+'
+ end
+
+ with_them do
+ it 'returns the expected formatted limited count' do
+ expect(results.formatted_limited_count(count)).to eq(expected)
+ end
+ end
+ end
+
context "when count_limit is lower than total amount" do
before do
allow(results).to receive(:count_limit).and_return(1)
diff --git a/spec/lib/gitlab/snippet_search_results_spec.rb b/spec/lib/gitlab/snippet_search_results_spec.rb
index b661a894c0c..35df38f052b 100644
--- a/spec/lib/gitlab/snippet_search_results_spec.rb
+++ b/spec/lib/gitlab/snippet_search_results_spec.rb
@@ -16,4 +16,22 @@ describe Gitlab::SnippetSearchResults do
expect(results.snippet_blobs_count).to eq(1)
end
end
+
+ describe '#formatted_count' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:scope, :count_method, :expected) do
+ 'snippet_titles' | :snippet_titles_count | '1234'
+ 'snippet_blobs' | :snippet_blobs_count | '1234'
+ 'projects' | :limited_projects_count | '1000+'
+ 'unknown' | nil | nil
+ end
+
+ with_them do
+ it 'returns the expected formatted count' do
+ expect(results).to receive(count_method).and_return(1234) if count_method
+ expect(results.formatted_count(scope)).to eq(expected)
+ end
+ end
+ end
end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 782a84f922b..2cb4f222ea4 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -172,6 +172,19 @@ describe Member do
it { expect(described_class.non_request).to include @accepted_request_member }
end
+ describe '.search_invite_email' do
+ it 'returns only members the matching e-mail' do
+ create(:group_member, :invited)
+
+ invited = described_class.search_invite_email(@invited_member.invite_email)
+
+ expect(invited.count).to eq(1)
+ expect(invited.first).to eq(@invited_member)
+
+ expect(described_class.search_invite_email('bad-email@example.com').count).to eq(0)
+ end
+ end
+
describe '.developers' do
subject { described_class.developers.to_a }
diff --git a/spec/presenters/blobs/unfold_presenter_spec.rb b/spec/presenters/blobs/unfold_presenter_spec.rb
index 1534c572b30..ab3f8080257 100644
--- a/spec/presenters/blobs/unfold_presenter_spec.rb
+++ b/spec/presenters/blobs/unfold_presenter_spec.rb
@@ -39,6 +39,21 @@ describe Blobs::UnfoldPresenter do
expect(result.indent).to eq(0)
end
end
+
+ context 'when to is -1' do
+ let(:params) { { full: false, since: 2, to: -1, bottom: true, offset: 1, indent: 1 } }
+
+ it 'sets other attributes' do
+ result = subject
+
+ expect(result.full?).to eq(false)
+ expect(result.since).to eq(2)
+ expect(result.to).to eq(blob.lines.size)
+ expect(result.bottom).to eq(false)
+ expect(result.offset).to eq(0)
+ expect(result.indent).to eq(0)
+ end
+ end
end
describe '#diff_lines' do
@@ -83,8 +98,9 @@ describe Blobs::UnfoldPresenter do
end
end
- context 'when since is greater than 1' do
- let(:params) { { since: 5, to: 10, offset: 10 } }
+ context 'when "since" is greater than 1' do
+ let(:default_params) { { since: 5, to: 10, offset: 10 } }
+ let(:params) { default_params }
it 'adds top match line' do
line = subject.diff_lines.first
@@ -93,6 +109,38 @@ describe Blobs::UnfoldPresenter do
expect(line.old_pos).to eq(5)
expect(line.new_pos).to eq(5)
end
+
+ context '"to" is higher than blob size' do
+ let(:params) { default_params.merge(to: total_lines + 10, bottom: true) }
+
+ it 'does not add bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to be_nil
+ end
+ end
+
+ context '"to" is equal to blob size' do
+ let(:params) { default_params.merge(to: total_lines, bottom: true) }
+
+ it 'does not add bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to be_nil
+ end
+ end
+
+ context '"to" is less than blob size' do
+ let(:params) { default_params.merge(to: total_lines - 3, bottom: true) }
+
+ it 'adds bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to eq('match')
+ expect(line.old_pos).to eq(total_lines - 3 - params[:offset])
+ expect(line.new_pos).to eq(total_lines - 3)
+ end
+ end
end
context 'when "to" is less than blob size' do
@@ -116,6 +164,22 @@ describe Blobs::UnfoldPresenter do
expect(line.type).to be_nil
end
end
+
+ context 'when "to" is "-1"' do
+ let(:params) { { since: 10, to: -1, offset: 10, bottom: true } }
+
+ it 'does not add bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to be_nil
+ end
+
+ it 'last line is the latest blob line' do
+ line = subject.diff_lines.last
+
+ expect(line.text).to eq(total_lines.to_s)
+ end
+ end
end
describe '#lines' do
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 6bbaa410d56..82010dd283c 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -186,7 +186,10 @@ describe Projects::UpdateService do
expect_any_instance_of(ProjectWiki).to receive(:wiki).and_raise(ProjectWiki::CouldNotCreateWikiError)
expect_any_instance_of(described_class).to receive(:log_error).with("Could not create wiki for #{project.full_name}")
- expect(Gitlab::Metrics).to receive(:counter)
+
+ counter = double(:counter)
+ expect(Gitlab::Metrics).to receive(:counter).with(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki').and_return(counter)
+ expect(counter).to receive(:increment)
update_project(project, user, project_feature_attributes: { wiki_access_level: ProjectFeature::ENABLED })
end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index bf5f211b11c..c9714964fc9 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -226,7 +226,7 @@ describe QuickActions::InterpretService do
it 'returns the todo message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq('Added a todo.')
+ expect(message).to eq('Added a To Do.')
end
end
@@ -242,7 +242,7 @@ describe QuickActions::InterpretService do
TodoService.new.mark_todo(issuable, developer)
_, _, message = service.execute(content, issuable)
- expect(message).to eq('Marked to do as done.')
+ expect(message).to eq('Marked To Do as done.')
end
end
@@ -453,7 +453,7 @@ describe QuickActions::InterpretService do
it 'returns the lock discussion message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq('Locked the discussion')
+ expect(message).to eq('Locked the discussion.')
end
end
@@ -470,7 +470,7 @@ describe QuickActions::InterpretService do
it 'returns the unlock discussion message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq('Unlocked the discussion')
+ expect(message).to eq('Unlocked the discussion.')
end
end
@@ -570,7 +570,7 @@ describe QuickActions::InterpretService do
it 'returns move issue failure message when the referenced issue is not found' do
_, _, message = service.execute('/move invalid', issue)
- expect(message).to eq("Move this issue failed because target project doesn't exists")
+ expect(message).to eq("Failed to move this issue because target project doesn't exist.")
end
end
@@ -584,7 +584,7 @@ describe QuickActions::InterpretService do
it 'returns the confidential message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq('Made this issue confidential')
+ expect(message).to eq('Made this issue confidential.')
end
end
@@ -783,7 +783,7 @@ describe QuickActions::InterpretService do
end
end
- it_behaves_like 'empty command', "Assign command failed because no user was found" do
+ it_behaves_like 'empty command', "Failed to assign a user because no user was found." do
let(:content) { '/assign @abcd1234' }
let(:issuable) { issue }
end
@@ -1217,12 +1217,12 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
- it_behaves_like 'empty command', 'Mark as duplicate failed because referenced issue was not found' do
+ it_behaves_like 'empty command', 'Failed to mark this issue as a duplicate because referenced issue was not found.' do
let(:content) { "/duplicate imaginary#1234" }
let(:issuable) { issue }
end
- it_behaves_like 'empty command', 'Mark as duplicate failed because referenced issue was not found' do
+ it_behaves_like 'empty command', 'Failed to mark this issue as a duplicate because referenced issue was not found.' do
let(:other_project) { create(:project, :private) }
let(:issue_duplicate) { create(:issue, project: other_project) }
@@ -1287,7 +1287,7 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
- it_behaves_like 'empty command', 'Mark as duplicate failed because referenced issue was not found' do
+ it_behaves_like 'empty command', 'Failed to mark this issue as a duplicate because referenced issue was not found.' do
let(:content) { '/duplicate #{issue.to_reference}' }
let(:issuable) { issue }
end
@@ -1463,19 +1463,19 @@ describe QuickActions::InterpretService do
context 'if the given label does not exist' do
let(:issuable) { issue }
let(:content) { '/board_move ~"Fake Label"' }
- it_behaves_like 'empty command', 'Move this issue failed because you need to specify only one label.'
+ it_behaves_like 'empty command', 'Failed to move this issue because label was not found.'
end
context 'if multiple labels are given' do
let(:issuable) { issue }
let(:content) { %{/board_move ~"#{inreview.title}" ~"#{todo.title}"} }
- it_behaves_like 'empty command', 'Move this issue failed because you need to specify only one label.'
+ it_behaves_like 'empty command', 'Failed to move this issue because only a single label can be provided.'
end
context 'if the given label is not a list on the board' do
let(:issuable) { issue }
let(:content) { %{/board_move ~"#{bug.title}"} }
- it_behaves_like 'empty command'
+ it_behaves_like 'empty command', 'Failed to move this issue because label was not found.'
end
context 'if issuable is not an Issue' do
@@ -1552,7 +1552,7 @@ describe QuickActions::InterpretService do
it 'returns the create_merge_request message' do
_, _, message = service.execute(content, issuable)
- expect(message).to eq("Created branch '#{branch_name}' and a merge request to resolve this issue")
+ expect(message).to eq("Created branch '#{branch_name}' and a merge request to resolve this issue.")
end
end
end
@@ -1814,13 +1814,13 @@ describe QuickActions::InterpretService do
it 'uses the default branch name' do
_, explanations = service.explain(content, issue)
- expect(explanations).to eq(['Creates a branch and a merge request to resolve this issue'])
+ expect(explanations).to eq(['Creates a branch and a merge request to resolve this issue.'])
end
it 'returns the execution message using the default branch name' do
_, _, message = service.execute(content, issue)
- expect(message).to eq('Created a branch and a merge request to resolve this issue')
+ expect(message).to eq('Created a branch and a merge request to resolve this issue.')
end
end
@@ -1830,13 +1830,13 @@ describe QuickActions::InterpretService do
it 'uses the given branch name' do
_, explanations = service.explain(content, issue)
- expect(explanations).to eq(["Creates branch 'foo' and a merge request to resolve this issue"])
+ expect(explanations).to eq(["Creates branch 'foo' and a merge request to resolve this issue."])
end
it 'returns the execution message using the given branch name' do
_, _, message = service.execute(content, issue)
- expect(message).to eq("Created branch 'foo' and a merge request to resolve this issue")
+ expect(message).to eq("Created branch 'foo' and a merge request to resolve this issue.")
end
end
end
diff --git a/spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb
index 85682b4919d..a37b2392d52 100644
--- a/spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb
@@ -40,7 +40,7 @@ shared_examples 'move quick action' do
wait_for_requests
- expect(page).to have_content "Move this issue failed because target project doesn't exists"
+ expect(page).to have_content "Failed to move this issue because target project doesn't exist."
expect(issue.reload).to be_open
end
end
diff --git a/yarn.lock b/yarn.lock
index 2a406b92c5f..ed1f06523c0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1003,10 +1003,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579"
integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA==
-"@gitlab/ui@5.14.0":
- version "5.14.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.14.0.tgz#850214cfc6bb57f7ce672dc1cc60ea3d39ad41f3"
- integrity sha512-JXUmk+hT4Rj2GBh0xAF43dYeloBEDX22rgeaDU6/RzD3JEA353yEm2+HOsBjPkQFDAh6Zp7OZSBuhDFrQe8sbg==
+"@gitlab/ui@5.15.0":
+ version "5.15.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.15.0.tgz#1ce92cfed77dcd63a90d751043b42b19e64431c9"
+ integrity sha512-YrYgERcmTxC+oP4GaY7onqvYgvTsyGCiiegQbZbXdNRLGGAmvfxWPzQRz8Ci9yIYkLvi0X2AV7BT8RTVOPQgXg==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1"