summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Hegyi <ahegyi@gitlab.com>2019-08-14 05:12:51 +0000
committerAdam Hegyi <ahegyi@gitlab.com>2019-08-14 05:12:51 +0000
commit60eb08db2e706b1072bc7cff1c69f5cd1a2cc5fa (patch)
treefa11cb8049762e4a8d8825b9c7bfe68586910f48
parent707c334d2a1d6e8c482f97c66498fb6265f8e812 (diff)
parentf9290e5b5282c3a245a58716a609aa10a47e9f92 (diff)
downloadgitlab-ce-12502-add-view-stats-to-cycle-analytics.tar.gz
# Conflicts: # lib/gitlab/usage_data.rb # spec/lib/gitlab/usage_data_spec.rb
-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--.gitlab/issue_templates/Feature proposal.md2
-rw-r--r--.gitlab/issue_templates/Test plan.md2
-rw-r--r--CHANGELOG.md20
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock10
-rw-r--r--LICENSE10
-rw-r--r--README.md21
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js2
-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/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/pages/builds.scss20
-rw-r--r--app/assets/stylesheets/pages/todos.scss2
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb8
-rw-r--r--app/controllers/projects/raw_controller.rb2
-rw-r--r--app/controllers/search_controller.rb9
-rw-r--r--app/finders/remote_mirror_finder.rb15
-rw-r--r--app/helpers/search_helper.rb34
-rw-r--r--app/mailers/emails/remote_mirrors.rb2
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/clusters/applications/helm.rb2
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/project_services/chat_message/pipeline_message.rb2
-rw-r--r--app/models/remote_mirror.rb57
-rw-r--r--app/models/repository.rb22
-rw-r--r--app/models/user.rb10
-rw-r--r--app/services/git/branch_hooks_service.rb4
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/projects/update_remote_mirror_service.rb53
-rw-r--r--app/services/projects/update_service.rb2
-rw-r--r--app/services/update_deployment_service.rb2
-rw-r--r--app/views/help/_shortcuts.html.haml154
-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/projects/mirrors/_mirror_repos.html.haml5
-rw-r--r--app/views/projects/update.js.haml2
-rw-r--r--app/views/search/_category.html.haml84
-rw-r--r--app/workers/all_queues.yml1
-rw-r--r--app/workers/build_process_worker.rb16
-rw-r--r--app/workers/post_receive.rb6
-rw-r--r--app/workers/remote_mirror_notification_worker.rb2
-rw-r--r--app/workers/repository_update_remote_mirror_worker.rb61
-rwxr-xr-xbin/changelog2
-rw-r--r--changelogs/unreleased/34414-update-personal-access-token-scope-descriptions-to-reflect-registry-permissions.yml6
-rw-r--r--changelogs/unreleased/44036-fix-someone-edited-the-issue-at-the-same-time-false-warning.yml (renamed from changelogs/unreleased/44036-someone-edited-the-issue-at-the-same-time-is-regularly-seen-despite-that-being-a-filthy-lie.yml)0
-rw-r--r--changelogs/unreleased/61787-broadcast-messages-colour-selector-provide-default-options-with-descriptive-labels.yml (renamed from changelogs/unreleased/61787-the-colour-selector-for-broadcast-messages-should-provide-a-few-default-options-with-descriptive-labels-like.yml)0
-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/65803-invalidate-branches-cache-on-refresh.yml5
-rw-r--r--changelogs/unreleased/66008-fix-project-image-in-slack-notifications.yml5
-rw-r--r--changelogs/unreleased/alipniagov-fix-wiki_can_not_be_created_total-counter.yml5
-rw-r--r--changelogs/unreleased/bump_helm_kubectl_gitlab.yml5
-rw-r--r--changelogs/unreleased/bvl-remote-mirror-exception-handling.yml6
-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/id-source-code-smau.yml5
-rw-r--r--changelogs/unreleased/improve-quick-action-messages.yml5
-rw-r--r--changelogs/unreleased/issue-61873-no-error-message-for-general-settings.yml5
-rw-r--r--changelogs/unreleased/load-search-counts-async.yml5
-rw-r--r--changelogs/unreleased/sh-fix-import-export-suggestions.yml5
-rw-r--r--changelogs/unreleased/sh-only-flush-tags-once-per-push.yml5
-rw-r--r--changelogs/unreleased/update-graphicsmagick-to-1-3-33.yml5
-rw-r--r--config.ru18
-rw-r--r--config/application.rb10
-rw-r--r--config/initializers/7_prometheus_metrics.rb19
-rw-r--r--config/locales/doorkeeper.en.yml2
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20190715173819_add_object_storage_flag_to_geo_node.rb21
-rw-r--r--db/schema.rb1
-rw-r--r--doc/administration/index.md5
-rw-r--r--doc/administration/monitoring/gitlab_instance_administration_project/index.md36
-rw-r--r--doc/administration/monitoring/index.md3
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md30
-rw-r--r--doc/administration/operations/unicorn.md2
-rw-r--r--doc/api/geo_nodes.md81
-rw-r--r--doc/api/releases/links.md10
-rw-r--r--doc/ci/README.md6
-rw-r--r--doc/development/contributing/merge_request_workflow.md31
-rw-r--r--doc/development/documentation/styleguide.md118
-rw-r--r--doc/development/fe_guide/event_tracking.md6
-rw-r--r--doc/development/fe_guide/index.md2
-rw-r--r--doc/development/understanding_explain_plans.md4
-rw-r--r--doc/install/installation.md2
-rw-r--r--doc/subscriptions/img/additional_minutes.png (renamed from doc/user/admin_area/settings/img/additional_minutes.png)bin12668 -> 12668 bytes
-rw-r--r--doc/subscriptions/img/buy_btn.png (renamed from doc/user/admin_area/settings/img/buy_btn.png)bin10917 -> 10917 bytes
-rw-r--r--doc/subscriptions/img/buy_minutes_card.png (renamed from doc/user/admin_area/settings/img/buy_minutes_card.png)bin11875 -> 11875 bytes
-rw-r--r--doc/subscriptions/index.md46
-rw-r--r--doc/update/README.md24
-rw-r--r--doc/update/upgrading_from_source.md14
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md33
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md46
-rw-r--r--doc/user/gitlab_com/index.md15
-rw-r--r--doc/user/permissions.md3
-rw-r--r--doc/user/profile/personal_access_tokens.md2
-rw-r--r--doc/user/project/import/bitbucket.md12
-rw-r--r--doc/user/project/import/bitbucket_server.md6
-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/merge_requests/merge_request_approvals.md17
-rw-r--r--doc/user/project/pipelines/schedules.md4
-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/expand_variables.rb18
-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/ci/config/entry/job.rb3
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_observer.rb4
-rw-r--r--lib/gitlab/config/entry/attributable.rb4
-rw-r--r--lib/gitlab/config/entry/validators.rb11
-rw-r--r--lib/gitlab/git_post_receive.rb12
-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/kubernetes/helm.rb4
-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--lib/gitlab/usage_data.rb3
-rw-r--r--lib/gitlab/usage_data_counters/source_code_counter.rb8
-rw-r--r--locale/gitlab.pot248
-rw-r--r--package.json2
-rw-r--r--spec/bin/changelog_spec.rb2
-rw-r--r--spec/controllers/import/bitbucket_server_controller_spec.rb14
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb6
-rw-r--r--spec/controllers/search_controller_spec.rb202
-rw-r--r--spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb39
-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/environments/confirm_rollback_modal_spec.js (renamed from spec/javascripts/environments/confirm_rollback_modal_spec.js)2
-rw-r--r--spec/frontend/environments/environment_rollback_spec.js (renamed from spec/javascripts/environments/environment_rollback_spec.js)30
-rw-r--r--spec/frontend/matchers.js38
-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/frontend/test_setup.js3
-rw-r--r--spec/helpers/search_helper_spec.rb44
-rw-r--r--spec/javascripts/jobs/components/job_log_spec.js4
-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/expand_variables_spec.rb175
-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/ci/config/entry/job_spec.rb40
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb4
-rw-r--r--spec/lib/gitlab/config/entry/attributable_spec.rb3
-rw-r--r--spec/lib/gitlab/git_post_receive_spec.rb95
-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/kubernetes/helm/pod_spec.rb2
-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/lib/gitlab/usage_data_counters/source_code_counter_spec.rb9
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb3
-rw-r--r--spec/models/clusters/applications/helm_spec.rb10
-rw-r--r--spec/models/project_services/chat_message/pipeline_message_spec.rb6
-rw-r--r--spec/models/project_spec.rb15
-rw-r--r--spec/models/remote_mirror_spec.rb10
-rw-r--r--spec/models/repository_spec.rb25
-rw-r--r--spec/policies/clusters/instance_policy_spec.rb4
-rw-r--r--spec/services/git/tag_push_service_spec.rb4
-rw-r--r--spec/services/projects/update_remote_mirror_service_spec.rb66
-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/policies/clusterable_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/quick_actions/issue/move_quick_action_shared_examples.rb2
-rw-r--r--spec/workers/build_process_worker_spec.rb26
-rw-r--r--spec/workers/post_receive_spec.rb89
-rw-r--r--spec/workers/repository_update_remote_mirror_worker_spec.rb111
-rw-r--r--yarn.lock8
187 files changed, 2324 insertions, 970 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/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md
index 8a49715e0e8..2ba6b68a53e 100644
--- a/.gitlab/issue_templates/Feature proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
@@ -26,7 +26,7 @@ Add all known Documentation Requirements here, per https://docs.gitlab.com/ee/de
### Testing
-<!-- What risks does this change pose? How might it affect the quality of the product? What additional test coverage or changes to tests will be needed? Will it require cross-browser testing? See the test engineering process for further guidelines: https://about.gitlab.com/handbook/engineering/quality/guidelines/test-engineering/ -->
+<!-- What risks does this change pose? How might it affect the quality of the product? What additional test coverage or changes to tests will be needed? Will it require cross-browser testing? See the test engineering process for further help: https://about.gitlab.com/handbook/engineering/quality/test-engineering/ -->
### What does success look like, and how can we measure that?
diff --git a/.gitlab/issue_templates/Test plan.md b/.gitlab/issue_templates/Test plan.md
index 3aedd5859d3..f194adebc87 100644
--- a/.gitlab/issue_templates/Test plan.md
+++ b/.gitlab/issue_templates/Test plan.md
@@ -89,7 +89,7 @@ New end-to-end and integration tests (Selenium and API) should be added to the
Please note if automated tests already exist.
-When adding new automated tests, please keep [testing levels](https://docs.gitlab.com/ce/development/testing_guide/testing_levels.html)
+When adding new automated tests, please keep [testing levels](https://docs.gitlab.com/ee/development/testing_guide/testing_levels.html)
in mind.
-->
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 6a8cf2981ca..55143693d5c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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 68c40cd19f7..6aa96d54abb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
@@ -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)
diff --git a/LICENSE b/LICENSE
index a90ea939517..216125d93c7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,12 @@
-Copyright GitLab B.V.
+Copyright (c) 2011-present GitLab B.V.
+
+Portions of this software are licensed as follows:
+
+* All content residing under the "doc/" directory of this repository is licensed under "Creative Commons: CC BY-SA 4.0 license".
+* All content that resides under the "ee/" directory of this repository, if that directory exists, is licensed under the license defined in "ee/LICENSE".
+* All client-side JavaScript (when served directly or after being compiled, arranged, augmented, or combined), is licensed under the "MIT Expat" license.
+* All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component.
+* Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 73bfcc988cc..054e2d02461 100644
--- a/README.md
+++ b/README.md
@@ -20,10 +20,6 @@ To see how GitLab looks please see the [features page on our website](https://ab
- Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
- Completely free and open source (MIT Expat license)
-## Hiring
-
-We're hiring developers, support people, and production engineers all the time, please see our [jobs page](https://about.gitlab.com/jobs/).
-
## Editions
There are two editions of GitLab:
@@ -31,6 +27,15 @@ There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/pricing/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
+## Licensing
+
+See the [LICENSE](LICENSE) file for licensing information as it pertains to
+files in this repository.
+
+## Hiring
+
+We're hiring developers, support people, and production engineers all the time, please see our [jobs page](https://about.gitlab.com/jobs/).
+
## Website
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
@@ -58,14 +63,6 @@ There are various other options to install GitLab, please refer to the [installa
GitLab is an open source project and we are very happy to accept community contributions. Please refer to [Contributing to GitLab page](https://about.gitlab.com/contributing/) for more details.
-## Licensing
-
-GitLab Community Edition (CE) is available freely under the MIT Expat license.
-
-All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component.
-
-All Documentation content that resides under the `doc/` directory of this repository is licensed under Creative Commons: CC BY-SA 4.0.
-
## Install a development environment
To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
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/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/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/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/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/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/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 3254229d9cb..c94fdd9483d 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -26,7 +26,7 @@ class Projects::RawController < Projects::ApplicationController
limiter.log_request(request, :raw_blob_request_limit, current_user)
flash[:alert] = _('You cannot access the raw file. Please wait a minute.')
- redirect_to project_blob_path(@project, File.join(@ref, @path))
+ redirect_to project_blob_path(@project, File.join(@ref, @path)), status: :too_many_requests
end
def raw_blob_request_limit
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/finders/remote_mirror_finder.rb b/app/finders/remote_mirror_finder.rb
deleted file mode 100644
index 420db0077aa..00000000000
--- a/app/finders/remote_mirror_finder.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-class RemoteMirrorFinder
- attr_accessor :params
-
- def initialize(params)
- @params = params
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def execute
- RemoteMirror.find_by(id: params[:id])
- end
- # rubocop: enable CodeReuse/ActiveRecord
-end
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/mailers/emails/remote_mirrors.rb b/app/mailers/emails/remote_mirrors.rb
index 2d8137843ec..f3938a052b0 100644
--- a/app/mailers/emails/remote_mirrors.rb
+++ b/app/mailers/emails/remote_mirrors.rb
@@ -3,7 +3,7 @@
module Emails
module RemoteMirrors
def remote_mirror_update_failed_email(remote_mirror_id, recipient_id)
- @remote_mirror = RemoteMirrorFinder.new(id: remote_mirror_id).execute
+ @remote_mirror = RemoteMirror.find_by_id(remote_mirror_id)
@project = @remote_mirror.project
mail(to: recipient(recipient_id, @project.group), subject: subject('Remote mirror update failed'))
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index ac88d9714ac..f705e67121f 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -384,7 +384,7 @@ module Ci
return unless has_environment?
strong_memoize(:expanded_environment_name) do
- ExpandVariables.expand(environment, simple_variables)
+ ExpandVariables.expand(environment, -> { simple_variables })
end
end
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index 3a175fec148..455cf200fbc 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -41,7 +41,7 @@ module Clusters
extra_apps = Clusters::Applications::Helm.where('EXISTS (?)', klass.select(1).where(cluster_id: cluster_id))
- applications = applications.present? ? applications.or(extra_apps) : extra_apps
+ applications = applications ? applications.or(extra_apps) : extra_apps
end
!applications.exists?
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/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
index 4edf263433f..a3793d9937b 100644
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ b/app/models/project_services/chat_message/pipeline_message.rb
@@ -68,7 +68,7 @@ module ChatMessage
title_link: pipeline_url,
fields: attachments_fields,
footer: project.name,
- footer_icon: project.avatar_url,
+ footer_icon: project.avatar_url(only_path: false),
ts: finished_at
}]
end
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 6b5605f9999..c9ee0653d86 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -4,6 +4,8 @@ class RemoteMirror < ApplicationRecord
include AfterCommitQueue
include MirrorAuthentication
+ MAX_FIRST_RUNTIME = 3.hours
+ MAX_INCREMENTAL_RUNTIME = 1.hour
PROTECTED_BACKOFF_DELAY = 1.minute
UNPROTECTED_BACKOFF_DELAY = 5.minutes
@@ -31,11 +33,18 @@ class RemoteMirror < ApplicationRecord
scope :enabled, -> { where(enabled: true) }
scope :started, -> { with_update_status(:started) }
- scope :stuck, -> { started.where('last_update_at < ? OR (last_update_at IS NULL AND updated_at < ?)', 1.hour.ago, 3.hours.ago) }
+
+ scope :stuck, -> do
+ started
+ .where('(last_update_started_at < ? AND last_update_at IS NOT NULL)',
+ MAX_INCREMENTAL_RUNTIME.ago)
+ .or(where('(last_update_started_at < ? AND last_update_at IS NULL)',
+ MAX_FIRST_RUNTIME.ago))
+ end
state_machine :update_status, initial: :none do
event :update_start do
- transition [:none, :finished, :failed] => :started
+ transition any => :started
end
event :update_finish do
@@ -46,9 +55,14 @@ class RemoteMirror < ApplicationRecord
transition started: :failed
end
+ event :update_retry do
+ transition started: :to_retry
+ end
+
state :started
state :finished
state :failed
+ state :to_retry
after_transition any => :started do |remote_mirror, _|
Gitlab::Metrics.add_event(:remote_mirrors_running)
@@ -138,16 +152,27 @@ class RemoteMirror < ApplicationRecord
end
def updated_since?(timestamp)
- last_update_started_at && last_update_started_at > timestamp && !update_failed?
+ return false if failed?
+
+ last_update_started_at && last_update_started_at > timestamp
end
def mark_for_delete_if_blank_url
mark_for_destruction if url.blank?
end
- def mark_as_failed(error_message)
- update_column(:last_error, Gitlab::UrlSanitizer.sanitize(error_message))
- update_fail
+ def update_error_message(error_message)
+ self.last_error = Gitlab::UrlSanitizer.sanitize(error_message)
+ end
+
+ def mark_for_retry!(error_message)
+ update_error_message(error_message)
+ update_retry!
+ end
+
+ def mark_as_failed!(error_message)
+ update_error_message(error_message)
+ update_fail!
end
def url=(value)
@@ -190,6 +215,18 @@ class RemoteMirror < ApplicationRecord
update_column(:error_notification_sent, true)
end
+ def backoff_delay
+ if self.only_protected_branches
+ PROTECTED_BACKOFF_DELAY
+ else
+ UNPROTECTED_BACKOFF_DELAY
+ end
+ end
+
+ def max_runtime
+ last_update_at.present? ? MAX_INCREMENTAL_RUNTIME : MAX_FIRST_RUNTIME
+ end
+
private
def store_credentials
@@ -219,14 +256,6 @@ class RemoteMirror < ApplicationRecord
self.last_update_started_at >= Time.now - backoff_delay
end
- def backoff_delay
- if self.only_protected_branches
- PROTECTED_BACKOFF_DELAY
- else
- UNPROTECTED_BACKOFF_DELAY
- end
- end
-
def reset_fields
update_columns(
last_error: nil,
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a89f573e3d6..9d45a12fa6e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -418,25 +418,29 @@ class Repository
end
# Runs code before pushing (= creating or removing) a tag.
+ #
+ # Note that this doesn't expire the tags. You may need to call
+ # expire_caches_for_tags or expire_tags_cache.
def before_push_tag
+ repository_event(:push_tag)
+ end
+
+ def expire_caches_for_tags
expire_statistics_caches
expire_emptiness_caches
expire_tags_cache
-
- repository_event(:push_tag)
end
# Runs code before removing a tag.
def before_remove_tag
- expire_tags_cache
- expire_statistics_caches
+ expire_caches_for_tags
repository_event(:remove_tag)
end
# Runs code after removing a tag.
def after_remove_tag
- expire_tags_cache
+ expire_caches_for_tags
end
# Runs code after the HEAD of a repository is changed.
@@ -460,8 +464,8 @@ class Repository
end
# Runs code after a new branch has been created.
- def after_create_branch
- expire_branches_cache
+ def after_create_branch(expire_cache: true)
+ expire_branches_cache if expire_cache
repository_event(:push_branch)
end
@@ -474,8 +478,8 @@ class Repository
end
# Runs code after an existing branch has been removed.
- def after_remove_branch
- expire_branches_cache
+ def after_remove_branch(expire_cache: true)
+ expire_branches_cache if expire_cache
end
def method_missing(msg, *args, &block)
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/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index c41f445c3c4..431a5aedf2e 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -63,7 +63,7 @@ module Git
end
def branch_create_hooks
- project.repository.after_create_branch
+ project.repository.after_create_branch(expire_cache: false)
project.after_create_default_branch if default_branch?
end
@@ -78,7 +78,7 @@ module Git
end
def branch_remove_hooks
- project.repository.after_remove_branch
+ project.repository.after_remove_branch(expire_cache: false)
end
# Schedules processing of commit messages
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_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb
index 1244a0f72a7..13a467a3ef9 100644
--- a/app/services/projects/update_remote_mirror_service.rb
+++ b/app/services/projects/update_remote_mirror_service.rb
@@ -2,31 +2,52 @@
module Projects
class UpdateRemoteMirrorService < BaseService
- attr_reader :errors
+ MAX_TRIES = 3
- def execute(remote_mirror)
+ def execute(remote_mirror, tries)
return success unless remote_mirror.enabled?
- errors = []
+ update_mirror(remote_mirror)
- begin
- remote_mirror.ensure_remote!
- repository.fetch_remote(remote_mirror.remote_name, ssh_auth: remote_mirror, no_tags: true)
+ success
+ rescue Gitlab::Git::CommandError => e
+ # This happens if one of the gitaly calls above fail, for example when
+ # branches have diverged, or the pre-receive hook fails.
+ retry_or_fail(remote_mirror, e.message, tries)
- opts = {}
- if remote_mirror.only_protected_branches?
- opts[:only_branches_matching] = project.protected_branches.select(:name).map(&:name)
- end
+ error(e.message)
+ rescue => e
+ remote_mirror.mark_as_failed!(e.message)
+ raise e
+ end
+
+ private
+
+ def update_mirror(remote_mirror)
+ remote_mirror.update_start!
+
+ remote_mirror.ensure_remote!
+ repository.fetch_remote(remote_mirror.remote_name, ssh_auth: remote_mirror, no_tags: true)
- remote_mirror.update_repository(opts)
- rescue => e
- errors << e.message.strip
+ opts = {}
+ if remote_mirror.only_protected_branches?
+ opts[:only_branches_matching] = project.protected_branches.select(:name).map(&:name)
end
- if errors.present?
- error(errors.join("\n\n"))
+ remote_mirror.update_repository(opts)
+
+ remote_mirror.update_finish!
+ end
+
+ def retry_or_fail(mirror, message, tries)
+ if tries < MAX_TRIES
+ mirror.mark_for_retry!(message)
else
- success
+ # It's not likely we'll be able to recover from this ourselves, so we'll
+ # notify the users of the problem, and don't trigger any sidekiq retries
+ # Instead, we'll wait for the next change to try the push again, or until
+ # a user manually retries.
+ mirror.mark_as_failed!(message)
end
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/services/update_deployment_service.rb b/app/services/update_deployment_service.rb
index 49a7d0178f4..dcafebae52d 100644
--- a/app/services/update_deployment_service.rb
+++ b/app/services/update_deployment_service.rb
@@ -42,7 +42,7 @@ class UpdateDeploymentService
return unless environment_url
@expanded_environment_url =
- ExpandVariables.expand(environment_url, variables)
+ ExpandVariables.expand(environment_url, -> { variables })
end
def environment_url
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 0624f66b3a5..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,80 +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
+ %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
+ %td= _('Move to previous file')
%tr
%td.shortcut
%kbd n
- %td= _("Move to next unresolved discussion")
+ %td= _('Move to next unresolved discussion')
%tr
%td.shortcut
%kbd p
- %td= _("Move to previous unresolved discussion")
- %tbody.hidden-shortcut{ style: 'display:none' }
+ %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/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 280ec6d715b..eb100e5cf47 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -43,7 +43,8 @@
= _('Mirrored repositories')
= render_if_exists 'projects/mirrors/mirrored_repositories_count'
%th= _('Direction')
- %th= _('Last update')
+ %th= _('Last update attempt')
+ %th= _('Last successful update')
%th
%th
%tbody.js-mirrors-table-body
@@ -53,6 +54,8 @@
%tr.qa-mirrored-repository-row.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?) }
%td.qa-mirror-repository-url= mirror.safe_url
%td= _('Push')
+ %td
+ = mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never')
%td.qa-mirror-last-update-at= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
%td
- if mirror.disabled?
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index 70f1bf8ef46..c5eecc900b2 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -4,7 +4,7 @@
location.reload();
- else
:plain
- $(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
+ $(".flash-container").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide();
$('.project-edit-container').show();
$('.edit-project .js-btn-success-general-project-settings').enable();
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/workers/all_queues.yml b/app/workers/all_queues.yml
index 400becdd023..991a177018e 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -88,7 +88,6 @@
- pipeline_processing:ci_build_prepare
- pipeline_processing:build_queue
- pipeline_processing:build_success
-- pipeline_processing:build_process
- pipeline_processing:pipeline_process
- pipeline_processing:pipeline_success
- pipeline_processing:pipeline_update
diff --git a/app/workers/build_process_worker.rb b/app/workers/build_process_worker.rb
deleted file mode 100644
index 9cd9519df1f..00000000000
--- a/app/workers/build_process_worker.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-class BuildProcessWorker
- include ApplicationWorker
- include PipelineQueue
-
- queue_namespace :pipeline_processing
-
- # rubocop: disable CodeReuse/ActiveRecord
- def perform(build_id)
- CommitStatus.find_by(id: build_id).try do |build|
- build.pipeline.process!([build_id])
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index dba7837bd12..622bd6f1f48 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -42,6 +42,11 @@ class PostReceive
user = identify_user(post_received)
return false unless user
+ # Expire the branches cache so we have updated data for this push
+ post_received.project.repository.expire_branches_cache if post_received.includes_branches?
+ # We only need to expire tags once per push
+ post_received.project.repository.expire_caches_for_tags if post_received.includes_tags?
+
post_received.enum_for(:changes_refs).with_index do |(oldrev, newrev, ref), index|
service_klass =
if Gitlab::Git.tag_ref?(ref)
@@ -72,6 +77,7 @@ class PostReceive
def after_project_changes_hooks(post_received, user, refs, changes)
hook_data = Gitlab::DataBuilder::Repository.update(post_received.project, user, changes, refs)
SystemHooksService.new.execute_hooks(hook_data, :repository_update_hooks)
+ Gitlab::UsageDataCounters::SourceCodeCounter.count(:pushes)
end
def process_wiki_changes(post_received)
diff --git a/app/workers/remote_mirror_notification_worker.rb b/app/workers/remote_mirror_notification_worker.rb
index 5bafe8e2046..368abfeda99 100644
--- a/app/workers/remote_mirror_notification_worker.rb
+++ b/app/workers/remote_mirror_notification_worker.rb
@@ -4,7 +4,7 @@ class RemoteMirrorNotificationWorker
include ApplicationWorker
def perform(remote_mirror_id)
- remote_mirror = RemoteMirrorFinder.new(id: remote_mirror_id).execute
+ remote_mirror = RemoteMirror.find_by_id(remote_mirror_id)
# We check again if there's an error because a newer run since this job was
# fired could've completed successfully.
diff --git a/app/workers/repository_update_remote_mirror_worker.rb b/app/workers/repository_update_remote_mirror_worker.rb
index 03a7ff2cd7a..d13c7641eb3 100644
--- a/app/workers/repository_update_remote_mirror_worker.rb
+++ b/app/workers/repository_update_remote_mirror_worker.rb
@@ -1,50 +1,53 @@
# frozen_string_literal: true
class RepositoryUpdateRemoteMirrorWorker
- UpdateAlreadyInProgressError = Class.new(StandardError)
UpdateError = Class.new(StandardError)
include ApplicationWorker
+ include Gitlab::ExclusiveLeaseHelpers
sidekiq_options retry: 3, dead: false
- sidekiq_retry_in { |count| 30 * count }
+ LOCK_WAIT_TIME = 30.seconds
+ MAX_TRIES = 3
- sidekiq_retries_exhausted do |msg, _|
- Sidekiq.logger.warn "Failed #{msg['class']} with #{msg['args']}: #{msg['error_message']}"
- end
-
- def perform(remote_mirror_id, scheduled_time)
- remote_mirror = RemoteMirrorFinder.new(id: remote_mirror_id).execute
+ def perform(remote_mirror_id, scheduled_time, tries = 0)
+ remote_mirror = RemoteMirror.find_by_id(remote_mirror_id)
+ return unless remote_mirror
return if remote_mirror.updated_since?(scheduled_time)
- raise UpdateAlreadyInProgressError if remote_mirror.update_in_progress?
+ # If the update is already running, wait for it to finish before running again
+ # This will wait for a total of 90 seconds in 3 steps
+ in_lock(remote_mirror_update_lock(remote_mirror.id),
+ retries: 3,
+ ttl: remote_mirror.max_runtime,
+ sleep_sec: LOCK_WAIT_TIME) do
+ update_mirror(remote_mirror, scheduled_time, tries)
+ end
+ rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
+ # If an update runs longer than 1.5 minutes, we'll reschedule it
+ # with a backoff. The next run will check if the previous update would
+ # include the changes that triggered this update and become a no-op.
+ self.class.perform_in(remote_mirror.backoff_delay, remote_mirror.id, scheduled_time, tries)
+ end
- remote_mirror.update_start
+ private
- project = remote_mirror.project
+ def update_mirror(mirror, scheduled_time, tries)
+ project = mirror.project
current_user = project.creator
- result = Projects::UpdateRemoteMirrorService.new(project, current_user).execute(remote_mirror)
- raise UpdateError, result[:message] if result[:status] == :error
-
- remote_mirror.update_finish
- rescue UpdateAlreadyInProgressError
- raise
- rescue UpdateError => ex
- fail_remote_mirror(remote_mirror, ex.message)
- raise
- rescue => ex
- return unless remote_mirror
+ result = Projects::UpdateRemoteMirrorService.new(project, current_user).execute(mirror, tries)
- fail_remote_mirror(remote_mirror, ex.message)
- raise UpdateError, "#{ex.class}: #{ex.message}"
+ if result[:status] == :error && mirror.to_retry?
+ schedule_retry(mirror, scheduled_time, tries)
+ end
end
- private
-
- def fail_remote_mirror(remote_mirror, message)
- remote_mirror.mark_as_failed(message)
+ def remote_mirror_update_lock(mirror_id)
+ [self.class.name, mirror_id].join(':')
+ end
- Rails.logger.error(message) # rubocop:disable Gitlab/RailsLogger
+ def schedule_retry(mirror, scheduled_time, tries)
+ self.class.perform_in(mirror.backoff_delay, mirror.id, scheduled_time, tries + 1)
end
end
diff --git a/bin/changelog b/bin/changelog
index 328d9495b96..ec068d06507 100755
--- a/bin/changelog
+++ b/bin/changelog
@@ -23,7 +23,7 @@ module ChangelogHelpers
Abort = Class.new(StandardError)
Done = Class.new(StandardError)
- MAX_FILENAME_LENGTH = 140 # ecryptfs has a limit of 140 characters
+ MAX_FILENAME_LENGTH = 99 # GNU tar has a 99 character limit
def capture_stdout(cmd)
output = IO.popen(cmd, &:read)
diff --git a/changelogs/unreleased/34414-update-personal-access-token-scope-descriptions-to-reflect-registry-permissions.yml b/changelogs/unreleased/34414-update-personal-access-token-scope-descriptions-to-reflect-registry-permissions.yml
new file mode 100644
index 00000000000..f0cc7fe9b6d
--- /dev/null
+++ b/changelogs/unreleased/34414-update-personal-access-token-scope-descriptions-to-reflect-registry-permissions.yml
@@ -0,0 +1,6 @@
+---
+title: Updated the personal access token api scope description to reflect the permissions
+ it grants
+merge_request: 31759
+author:
+type: other
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-fix-someone-edited-the-issue-at-the-same-time-false-warning.yml
index 674d53286e6..674d53286e6 100644
--- 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-fix-someone-edited-the-issue-at-the-same-time-false-warning.yml
diff --git a/changelogs/unreleased/61787-the-colour-selector-for-broadcast-messages-should-provide-a-few-default-options-with-descriptive-labels-like.yml b/changelogs/unreleased/61787-broadcast-messages-colour-selector-provide-default-options-with-descriptive-labels.yml
index ec6e9c5aff8..ec6e9c5aff8 100644
--- a/changelogs/unreleased/61787-the-colour-selector-for-broadcast-messages-should-provide-a-few-default-options-with-descriptive-labels-like.yml
+++ b/changelogs/unreleased/61787-broadcast-messages-colour-selector-provide-default-options-with-descriptive-labels.yml
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/65803-invalidate-branches-cache-on-refresh.yml b/changelogs/unreleased/65803-invalidate-branches-cache-on-refresh.yml
new file mode 100644
index 00000000000..217db8aa05a
--- /dev/null
+++ b/changelogs/unreleased/65803-invalidate-branches-cache-on-refresh.yml
@@ -0,0 +1,5 @@
+---
+title: Invalidate branches cache on PostReceive
+merge_request: 31653
+author:
+type: fixed
diff --git a/changelogs/unreleased/66008-fix-project-image-in-slack-notifications.yml b/changelogs/unreleased/66008-fix-project-image-in-slack-notifications.yml
new file mode 100644
index 00000000000..df0ac649ac1
--- /dev/null
+++ b/changelogs/unreleased/66008-fix-project-image-in-slack-notifications.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project avatar image in Slack pipeline notifications
+merge_request: 31788
+author:
+type: fixed
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/bump_helm_kubectl_gitlab.yml b/changelogs/unreleased/bump_helm_kubectl_gitlab.yml
new file mode 100644
index 00000000000..d768462e130
--- /dev/null
+++ b/changelogs/unreleased/bump_helm_kubectl_gitlab.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Helm to 2.14.3 and kubectl to 1.11.10 for Kubernetes integration
+merge_request: 31716
+author:
+type: other
diff --git a/changelogs/unreleased/bvl-remote-mirror-exception-handling.yml b/changelogs/unreleased/bvl-remote-mirror-exception-handling.yml
new file mode 100644
index 00000000000..962376086b0
--- /dev/null
+++ b/changelogs/unreleased/bvl-remote-mirror-exception-handling.yml
@@ -0,0 +1,6 @@
+---
+title: Retry push mirrors faster when running concurrently, improve error handling
+ when push mirrors fail
+merge_request: 31247
+author:
+type: changed
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/id-source-code-smau.yml b/changelogs/unreleased/id-source-code-smau.yml
new file mode 100644
index 00000000000..6ba5068544e
--- /dev/null
+++ b/changelogs/unreleased/id-source-code-smau.yml
@@ -0,0 +1,5 @@
+---
+title: Add usage pings for source code pushes
+merge_request: 31734
+author:
+type: added
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/issue-61873-no-error-message-for-general-settings.yml b/changelogs/unreleased/issue-61873-no-error-message-for-general-settings.yml
new file mode 100644
index 00000000000..606c60721b8
--- /dev/null
+++ b/changelogs/unreleased/issue-61873-no-error-message-for-general-settings.yml
@@ -0,0 +1,5 @@
+---
+title: error message for general settings
+merge_request: 31636
+author: Mesut Güneş
+type: fixed
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-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/sh-only-flush-tags-once-per-push.yml b/changelogs/unreleased/sh-only-flush-tags-once-per-push.yml
new file mode 100644
index 00000000000..502fc22ebbd
--- /dev/null
+++ b/changelogs/unreleased/sh-only-flush-tags-once-per-push.yml
@@ -0,0 +1,5 @@
+---
+title: Only expire tag cache once per push
+merge_request: 31641
+author:
+type: performance
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..2554dd8cca2 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -41,11 +41,6 @@ module Gitlab
#{config.root}/app/models/hooks
#{config.root}/app/models/members
#{config.root}/app/models/project_services
- #{config.root}/app/workers/concerns
- #{config.root}/app/policies/concerns
- #{config.root}/app/services/concerns
- #{config.root}/app/serializers/concerns
- #{config.root}/app/finders/concerns
#{config.root}/app/graphql/resolvers/concerns
#{config.root}/app/graphql/mutations/concerns])
@@ -293,10 +288,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/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index a8234263275..258d8a99986 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -69,7 +69,7 @@ en:
email: Allows read-only access to the user's primary email address using OpenID Connect
scope_desc:
api:
- Grants complete read/write access to the API, including all groups and projects.
+ Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry.
read_user:
Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
read_repository:
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/20190715173819_add_object_storage_flag_to_geo_node.rb b/db/migrate/20190715173819_add_object_storage_flag_to_geo_node.rb
new file mode 100644
index 00000000000..2d3243f3357
--- /dev/null
+++ b/db/migrate/20190715173819_add_object_storage_flag_to_geo_node.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddObjectStorageFlagToGeoNode < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :geo_nodes, :sync_object_storage, :boolean, default: false
+ end
+
+ def down
+ remove_column :geo_nodes, :sync_object_storage
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 591758af0e4..003f12b5171 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1456,6 +1456,7 @@ ActiveRecord::Schema.define(version: 2019_08_06_071559) do
t.integer "container_repositories_max_capacity", default: 10, null: false
t.datetime_with_timezone "created_at"
t.datetime_with_timezone "updated_at"
+ t.boolean "sync_object_storage", default: false, null: false
t.index ["access_key"], name: "index_geo_nodes_on_access_key"
t.index ["name"], name: "index_geo_nodes_on_name", unique: true
t.index ["primary"], name: "index_geo_nodes_on_primary"
diff --git a/doc/administration/index.md b/doc/administration/index.md
index fb07b41eb8b..2b25e8efd23 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -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/gitlab_instance_administration_project/index.md b/doc/administration/monitoring/gitlab_instance_administration_project/index.md
new file mode 100644
index 00000000000..8e33cea6217
--- /dev/null
+++ b/doc/administration/monitoring/gitlab_instance_administration_project/index.md
@@ -0,0 +1,36 @@
+# GitLab instance administration project
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/56883) in GitLab 12.2.
+
+GitLab has been adding the ability for administrators to see insights into the health of
+their GitLab instance. In order to surface this experience in a native way, similar to how
+you would interact with an application deployed via GitLab, a base project called
+"GitLab Instance Administration" with
+[internal visibility](../../../public_access/public_access.md#internal-projects) will be
+added under a group called "GitLab Instance Administrators" specifically created for
+visualizing and configuring the monitoring of your GitLab instance.
+
+All administrators at the time of creation of the project and group will be added
+as maintainers of the group and project, and as an admin, you'll be able to add new
+members to the group in order to give them maintainer access to the project.
+
+This project will be used for self-monitoring your GitLab instance.
+
+## Connection to Prometheus
+
+The project will be automatically configured to connect to the
+[internal Prometheus](../prometheus/index.md) instance if the Prometheus
+instance is present (should be the case if GitLab was installed via Omnibus
+and you haven't disabled it).
+
+If that's not the case or if you have an external Prometheus instance or an HA setup,
+you should
+[configure it manually](../../../user/project/integrations/prometheus.md#manual-configuration-of-prometheus).
+
+## Taking action on Prometheus alerts **[ULTIMATE]**
+
+You can [add a webhook](../../../user/project/integrations/prometheus.md#external-prometheus-instances)
+to the Prometheus config in order for GitLab to receive notifications of any alerts.
+
+Once the webhook is setup, you can
+[take action on incoming alerts](../../../user/project/integrations/prometheus.md#taking-action-on-incidents-ultimate).
diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md
index fa0459b24ff..2b3daec42bd 100644
--- a/doc/administration/monitoring/index.md
+++ b/doc/administration/monitoring/index.md
@@ -2,6 +2,9 @@
Explore our features to monitor your GitLab instance:
+- [GitLab self-monitoring](gitlab_instance_administration_project/index.md): The
+ GitLab instance administration project helps to monitor the GitLab instance and
+ take action on alerts.
- [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance.
- [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products.
- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics.
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/api/geo_nodes.md b/doc/api/geo_nodes.md
index e3af5d60533..d0b33ab467f 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -10,7 +10,7 @@ GET /geo_nodes
```
```bash
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/geo_nodes
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/geo_nodes
```
Example response:
@@ -27,8 +27,15 @@ Example response:
"current": true,
"files_max_capacity": 10,
"repos_max_capacity": 25,
+ "container_repositories_max_capacity": 10,
"verification_max_capacity": 100,
- "clone_protocol": "http"
+ "clone_protocol": "http",
+ "web_edit_url": "https://primary.example.com/admin/geo/nodes/1/edit",
+ "_links": {
+ "self": "https://primary.example.com/api/v4/geo_nodes/1",
+ "status":"https://primary.example.com/api/v4/geo_nodes/1/status",
+ "repair":"https://primary.example.com/api/v4/geo_nodes/1/repair"
+ }
},
{
"id": 2,
@@ -40,8 +47,17 @@ Example response:
"current": false,
"files_max_capacity": 10,
"repos_max_capacity": 25,
+ "container_repositories_max_capacity": 10,
"verification_max_capacity": 100,
- "clone_protocol": "http"
+ "sync_object_storage": true,
+ "clone_protocol": "http",
+ "web_edit_url": "https://primary.example.com/admin/geo/nodes/2/edit",
+ "web_geo_projects_url": "https://secondary.example.com/admin/geo/projects",
+ "_links": {
+ "self":"https://primary.example.com/api/v4/geo_nodes/2",
+ "status":"https://primary.example.com/api/v4/geo_nodes/2/status",
+ "repair":"https://primary.example.com/api/v4/geo_nodes/2/repair"
+ }
}
]
```
@@ -53,7 +69,7 @@ GET /geo_nodes/:id
```
```bash
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/geo_nodes/1
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/geo_nodes/1
```
Example response:
@@ -69,8 +85,15 @@ Example response:
"current": true,
"files_max_capacity": 10,
"repos_max_capacity": 25,
+ "container_repositories_max_capacity": 10,
"verification_max_capacity": 100,
- "clone_protocol": "http"
+ "clone_protocol": "http",
+ "web_edit_url": "https://primary.example.com/admin/geo/nodes/1/edit",
+ "_links": {
+ "self": "https://primary.example.com/api/v4/geo_nodes/1",
+ "status":"https://primary.example.com/api/v4/geo_nodes/1/status",
+ "repair":"https://primary.example.com/api/v4/geo_nodes/1/repair"
+ }
}
```
@@ -84,16 +107,18 @@ _This can only be run against a primary Geo node._
PUT /geo_nodes/:id
```
-| Attribute | Type | Required | Description |
-|----------------------|---------|-----------|---------------------------------------------------------------------------|
-| `id` | integer | yes | The ID of the Geo node. |
-| `enabled` | boolean | no | Flag indicating if the Geo node is enabled. |
-| `name` | string | yes | The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`. |
-| `url` | string | yes | The user-facing URL of the Geo node. |
-| `internal_url` | string | no | The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.|
-| `files_max_capacity` | integer | no | Control the maximum concurrency of LFS/attachment backfill for this secondary node. |
-| `repos_max_capacity` | integer | no | Control the maximum concurrency of repository backfill for this secondary node. |
-| `verification_max_capacity` | integer | no | Control the maximum concurrency of verification for this node. |
+| Attribute | Type | Required | Description |
+|-----------------------------|---------|-----------|---------------------------------------------------------------------------|
+| `id` | integer | yes | The ID of the Geo node. |
+| `enabled` | boolean | no | Flag indicating if the Geo node is enabled. |
+| `name` | string | yes | The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`. |
+| `url` | string | yes | The user-facing URL of the Geo node. |
+| `internal_url` | string | no | The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.|
+| `files_max_capacity` | integer | no | Control the maximum concurrency of LFS/attachment backfill for this secondary node. |
+| `repos_max_capacity` | integer | no | Control the maximum concurrency of repository backfill for this secondary node. |
+| `verification_max_capacity` | integer | no | Control the maximum concurrency of verification for this node. |
+| `container_repositories_max_capacity` | integer | no | Control the maximum concurrency of container repository sync for this node. |
+| `sync_object_storage` | boolean | no | Flag indicating if the secondary Geo node will replicate blobs in Object Storage. |
Example response:
@@ -108,8 +133,17 @@ Example response:
"current": true,
"files_max_capacity": 10,
"repos_max_capacity": 25,
+ "container_repositories_max_capacity": 10,
"verification_max_capacity": 100,
- "clone_protocol": "http"
+ "sync_object_storage": true,
+ "clone_protocol": "http",
+ "web_edit_url": "https://primary.example.com/admin/geo/nodes/2/edit",
+ "web_geo_projects_url": "https://secondary.example.com/admin/geo/projects",
+ "_links": {
+ "self":"https://primary.example.com/api/v4/geo_nodes/2",
+ "status":"https://primary.example.com/api/v4/geo_nodes/2/status",
+ "repair":"https://primary.example.com/api/v4/geo_nodes/2/repair"
+ }
}
```
@@ -151,8 +185,15 @@ Example response:
"current": true,
"files_max_capacity": 10,
"repos_max_capacity": 25,
+ "container_repositories_max_capacity": 10,
"verification_max_capacity": 100,
- "clone_protocol": "http"
+ "clone_protocol": "http",
+ "web_edit_url": "https://primary.example.com/admin/geo/nodes/1/edit",
+ "_links": {
+ "self": "https://primary.example.com/api/v4/geo_nodes/1",
+ "status":"https://primary.example.com/api/v4/geo_nodes/1/status",
+ "repair":"https://primary.example.com/api/v4/geo_nodes/1/repair"
+ }
}
```
@@ -163,7 +204,7 @@ GET /geo_nodes/status
```
```bash
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/geo_nodes/status
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/geo_nodes/status
```
Example response:
@@ -314,7 +355,7 @@ GET /geo_nodes/:id/status
```
```bash
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/geo_nodes/2/status
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/geo_nodes/2/status
```
Example response:
@@ -388,7 +429,7 @@ GET /geo_nodes/current/failures
This endpoint uses [Pagination](README.md#pagination).
```bash
-curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/geo_nodes/current/failures
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://primary.example.com/api/v4/geo_nodes/current/failures
```
Example response:
diff --git a/doc/api/releases/links.md b/doc/api/releases/links.md
index 9c91264ed65..b1b75abe32f 100644
--- a/doc/api/releases/links.md
+++ b/doc/api/releases/links.md
@@ -21,7 +21,7 @@ GET /projects/:id/releases/:tag_name/assets/links
Example request:
```sh
-curl --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "http://localhost:3000/api/v4/projects/24/releases/v0.1/assets/links"
+curl --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "https://gitlab.example.com/api/v4/projects/24/releases/v0.1/assets/links"
```
Example response:
@@ -60,7 +60,7 @@ GET /projects/:id/releases/:tag_name/assets/links/:link_id
Example request:
```sh
-curl --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "http://localhost:3000/api/v4/projects/24/releases/v0.1/assets/links/1"
+curl --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "https://gitlab.example.com/api/v4/projects/24/releases/v0.1/assets/links/1"
```
Example response:
@@ -96,7 +96,7 @@ curl --request POST \
--header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" \
--data name="awesome-v0.2.dmg" \
--data url="http://192.168.10.15:3000" \
- "http://localhost:3000/api/v4/projects/24/releases/v0.1/assets/links"
+ "https://gitlab.example.com/api/v4/projects/24/releases/v0.1/assets/links"
```
Example response:
@@ -132,7 +132,7 @@ You have to specify at least one of `name` or `url`
Example request:
```sh
-curl --request PUT --data name="new name" --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "http://localhost:3000/api/v4/projects/24/releases/v0.1/assets/links/1"
+curl --request PUT --data name="new name" --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "https://gitlab.example.com/api/v4/projects/24/releases/v0.1/assets/links/1"
```
Example response:
@@ -163,7 +163,7 @@ DELETE /projects/:id/releases/:tag_name/assets/links/:link_id
Example request:
```sh
-curl --request DELETE --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "http://localhost:3000/api/v4/projects/24/releases/v0.1/assets/links/1"
+curl --request DELETE --header "PRIVATE-TOKEN: gDybLx3yrUK_HLp3qPjS" "https://gitlab.example.com/api/v4/projects/24/releases/v0.1/assets/links/1"
```
Example response:
diff --git a/doc/ci/README.md b/doc/ci/README.md
index f3006528d01..ca9d0aa61bd 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -16,7 +16,7 @@ through the [continuous methodologies](introduction/index.md#introduction-to-cic
NOTE: **Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.**
Watch our
["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/)
-webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
+webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development.
## Overview
@@ -67,11 +67,11 @@ to your needs:
For a broader overview, see the [CI/CD getting started](quick_start/README.md) guide.
Once you're familiar with how GitLab CI/CD works, see the
-[`. gitlab-ci.yml` full reference](yaml/README.md)
+[`.gitlab-ci.yml` full reference](yaml/README.md)
for all the attributes you can set and use.
NOTE: **Note:**
-GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](../user/admin_area/settings/continuous_integration.md#extra-shared-runners-pipeline-minutes-quota-free-only).
+GitLab CI/CD and [shared runners](runners/README.md#shared-specific-and-group-runners) are enabled in GitLab.com and available for all users, limited only to the [user's pipelines quota](../user/gitlab_com/index.md#shared-runners).
## Configuration
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/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 715d91c6db6..1b417d4c8c2 100644
--- a/doc/development/fe_guide/event_tracking.md
+++ b/doc/development/fe_guide/event_tracking.md
@@ -1,6 +1,8 @@
-# Event Tracking
+# Event tracking
-We use a tracking interface that wraps up [Snowplow](https://github.com/snowplow/snowplow) for tracking custom events. Snowplow implements page tracking, but also exposes custom event tracking.
+GitLab provides `Tracking`, an interface that wraps
+[Snowplow](https://github.com/snowplow/snowplow) for tracking custom events.
+It uses Snowplow's custom event tracking functions.
The tracking interface can be imported in JS files as follows:
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 6bf6113dd81..deaef8e768b 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -17,6 +17,8 @@ Working with our frontend assets requires Node (v8.10.0 or greater) and Yarn
For our currently-supported browsers, see our [requirements][requirements].
+Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers. Login to BrowserStack with the credentials saved in GitLab's [shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).
+
## Initiatives
Current high-level frontend goals are listed on [Frontend Epics](https://gitlab.com/groups/gitlab-org/-/epics?label_name%5B%5D=frontend).
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index 11aafd7b639..7c926c83a36 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -199,7 +199,7 @@ more common ones here.
A full list of all the available nodes and their descriptions can be found in
the [PostgreSQL source file
-"plannodes.h"](https://github.com/postgres/postgres/blob/master/src/include/nodes/plannodes.h)
+"plannodes.h"](https://gitlab.com/postgres/postgres/blob/master/src/include/nodes/plannodes.h)
### Seq Scan
@@ -224,7 +224,7 @@ used when we would read too much data from an index scan, but too little to
perform a sequential scan. A bitmap scan uses what is known as a [bitmap
index](https://en.wikipedia.org/wiki/Bitmap_index) to perform its work.
-The [source code of PostgreSQL](https://github.com/postgres/postgres/blob/1c2cb2744bf3d8ad751cd5cf3b347f10f48492b3/src/include/nodes/plannodes.h#L446-L457)
+The [source code of PostgreSQL](https://gitlab.com/postgres/postgres/blob/REL_11_STABLE/src/include/nodes/plannodes.h#L441)
states the following on bitmap scans:
> Bitmap Index Scan delivers a bitmap of potential tuple locations; it does not
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 72a3514e2d5..df6c485b1cb 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -172,7 +172,7 @@ sudo make install
# Download and compile from source
cd /tmp
curl --remote-name --location --progress https://www.kernel.org/pub/software/scm/git/git-2.22.0.tar.gz
-echo 'a4b7e4365bee43caa12a38d646d2c93743d755d1cea5eab448ffb40906c9da0b' git-2.22.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.22.0.tar.gz
+echo 'a4b7e4365bee43caa12a38d646d2c93743d755d1cea5eab448ffb40906c9da0b git-2.22.0.tar.gz' | shasum -a256 -c - && tar -xzf git-2.22.0.tar.gz
cd git-2.22.0/
./configure --with-libpcre
make prefix=/usr/local all
diff --git a/doc/user/admin_area/settings/img/additional_minutes.png b/doc/subscriptions/img/additional_minutes.png
index b159b98c9ce..b159b98c9ce 100644
--- a/doc/user/admin_area/settings/img/additional_minutes.png
+++ b/doc/subscriptions/img/additional_minutes.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/buy_btn.png b/doc/subscriptions/img/buy_btn.png
index 4fd05c0fba7..4fd05c0fba7 100644
--- a/doc/user/admin_area/settings/img/buy_btn.png
+++ b/doc/subscriptions/img/buy_btn.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/buy_minutes_card.png b/doc/subscriptions/img/buy_minutes_card.png
index cab098300cd..cab098300cd 100644
--- a/doc/user/admin_area/settings/img/buy_minutes_card.png
+++ b/doc/subscriptions/img/buy_minutes_card.png
Binary files differ
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index 9317ba0c71e..13c406727ab 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -221,6 +221,52 @@ The following table describes details of your subscription for groups:
| Subscription start date | Date your subscription started. If this is for a Free plan, is the date you transitioned off your group's paid plan. |
| Subscription end date | Date your current subscription will end. Does not apply to Free plans. |
+#### Extra Shared Runners pipeline minutes
+
+If you're using GitLab.com, you can purchase additional CI minutes so your
+pipelines will not be blocked after you have used all your CI minutes from your
+main quota. Additional minutes:
+
+- Are only used once the shared quota included in your subscription runs out.
+- Roll over month to month.
+
+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)
+
+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)
+
+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)
+
+Be aware that:
+
+1. If you have purchased extra CI minutes before the purchase of a paid plan,
+ we will calculate a pro-rated charge for your paid plan. That means you may
+ be charged for less than one year since your subscription was previously
+ created with the extra CI minutes.
+1. Once the extra CI minutes has been assigned to a Group they cannot be transferred
+ to a different Group.
+1. If you have some minutes used over your default quota, these minutes will
+ be deducted from your Additional Minutes quota immediately after your purchase of additional
+ minutes.
+
+##### What happens when my CI minutes run out
+
+When the CI minutes run out, an email is sent automatically to notify the owner(s)
+of the group/namespace, including a link to [purchase more minutes](https://customers.gitlab.com/plans).
+
+If you are not the owner of the group, you will need to contact them to let them know they need to
+[purchase more minutes](https://customers.gitlab.com/plans).
+
## Subscription changes and your data
When your subscription or trial expires, GitLab does not delete your data.
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/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md
index 7fbb4d84cfc..6faab685b26 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -4,6 +4,17 @@ type: reference
# Account and limit settings
+## Max attachment size
+
+You can change the maximum file size for attachments in comments and replies in GitLab.
+Navigate to **Admin Area (wrench icon) > Settings > General**, then expand **Account and Limit**.
+From here, you can increase or decrease by changing the value in `Maximum attachment size (MB)`.
+
+NOTE: **Note:**
+If you choose a size larger than what is currently configured for the web server,
+you will likely get errors. See the [troubleshooting section](#troubleshooting) for more
+details.
+
## Repository size limit **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/740) in [GitLab Enterprise Edition 8.12](https://about.gitlab.com/2016/09/22/gitlab-8-12-released/#limit-project-size-ee).
@@ -51,14 +62,18 @@ For details on manually purging files, see [reducing the repository size using G
NOTE: **Note:**
GitLab.com repository size [is set by GitLab](../../gitlab_com/index.md#repository-size-limit).
-<!-- ## Troubleshooting
+## Troubleshooting
+
+### 413 Request Entity Too Large
+
+If you are attaching a file to a comment or reply in GitLab and receive the `413 Request Entity Too Large`
+error, it is likely caused by having a [max attachment size](#max-attachment-size)
+larger than what the web server is configured to allow.
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+If you wanted to increase the max attachment size to 200m in a GitLab
+[Omnibus](https://docs.gitlab.com/omnibus/) install, for example, you might need to
+add the line below to `/etc/gitlab/gitlab.rb` before increasing the max attachment size:
-Each scenario can be a third-level heading, e.g. `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
+```
+nginx['client_max_body_size'] = "200m"
+```
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 43640f1b16a..bd76b052422 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -94,52 +94,6 @@ a group in the **Usage Quotas** page available to the group page settings list.
![Group pipelines quota](img/group_pipelines_quota.png)
-## Extra Shared Runners pipeline minutes quota **(FREE ONLY)**
-
-If you're using GitLab.com, you can purchase additional CI minutes so your
-pipelines will not be blocked after you have used all your CI minutes from your
-main quota. Additional minutes:
-
-- Are only used once the shared quota included in your subscription runs out.
-- Roll over month to month.
-
-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)
-
-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)
-
-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)
-
-Be aware that:
-
-1. If you have purchased extra CI minutes before the purchase of a paid plan,
- we will calculate a pro-rated charge for your paid plan. That means you may
- be charged for less than one year since your subscription was previously
- created with the extra CI minutes.
-1. Once the extra CI minutes has been assigned to a Group they cannot be transferred
- to a different Group.
-1. If you have some minutes used over your default quota, these minutes will
- be deducted from your Additional Minutes quota immediately after your purchase of additional
- minutes.
-
-## What happens when my CI minutes quota run out
-
-When the CI minutes quota run out, an email is sent automatically to notifies the owner(s) of the group/namespace which
-includes a link to [purchase more minutes](https://customers.gitlab.com/plans).
-
-If you are not the owner of the group, you will need to contact them to let them know they need to
-[purchase more minutes](https://customers.gitlab.com/plans).
-
## Archive jobs **(CORE ONLY)**
Archiving jobs is useful for reducing the CI/CD footprint on the system by
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 928950126da..c9fbd7effa0 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -81,13 +81,12 @@ IP based firewall can be configured by looking up all
## Shared Runners
-Shared Runners on GitLab.com run in [autoscale mode] and powered by
-Google Cloud Platform. Autoscaling means reduced
-waiting times to spin up CI/CD jobs, and isolated VMs for each project,
-thus maximizing security.
-They're free to use for public open source projects and limited to 2000 CI
-minutes per month per group for private projects. Read about all
-[GitLab.com plans](https://about.gitlab.com/pricing/).
+Shared Runners on GitLab.com run in [autoscale mode] and powered by Google Cloud Platform.
+Autoscaling means reduced waiting times to spin up CI/CD jobs, and isolated VMs for each project,
+thus maximizing security. They're free to use for public open source projects and limited
+to 2000 CI minutes per month per group for private projects. More minutes
+[can be purchased](../../subscriptions/index.md#extra-shared-runners-pipeline-minutes), if
+needed. Read about all [GitLab.com plans](https://about.gitlab.com/pricing/).
All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS and the latest Docker Engine
installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default
@@ -223,7 +222,7 @@ and the following environment variables:
| Setting | GitLab.com | Default |
|-------- |----------- |-------- |
-| `SIDEKIQ_MEMORY_KILLER_MAX_RSS` | `1000000` | `1000000` |
+| `SIDEKIQ_MEMORY_KILLER_MAX_RSS` | `1000000` | `2000000` |
| `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_SIGNAL` | `SIGKILL` | - |
| `SIDEKIQ_LOG_ARGUMENTS` | `1` | - |
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index d92435ef724..1d457099ebc 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -47,6 +47,7 @@ The following table depicts the various user permission levels in a project.
| View approved/blacklisted licenses **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View license management reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View Security reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
+| View Dependency list **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View [Design Management](project/issues/design_management.md) pages **(PREMIUM)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Pull project code | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
@@ -93,7 +94,7 @@ The following table depicts the various user permission levels in a project.
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
| Use security dashboard **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
-| View dependency list **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
+| View vulnerabilities in Dependency list **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create issue from vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Dismiss vulnerability **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Apply code change suggestions | | | ✓ | ✓ | ✓ |
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 248188a6457..d556daa3460 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -44,7 +44,7 @@ the following table.
| Scope | Introduced in | Description |
| ------------------ | ------------- | ----------- |
| `read_user` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951) | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed. |
-| `api` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951) | Grants complete access to the API and Container Registry (read/write). |
+| `api` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951) | Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry. |
| `read_registry` | [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845) | Allows to read (pull) [container registry] images if a project is private and authorization is required. |
| `sudo` | [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14838) | Allows performing API actions as any user in the system (if the authenticated user is an admin). |
| `read_repository` | [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17894) | Allows read-only access (pull) to the repository through git clone. |
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index 5abce39c093..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
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
index 4e1614d82a5..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
@@ -68,7 +70,7 @@ namespace that started the import process.
![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/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/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md
index 509576c8d6f..5161b25de99 100644
--- a/doc/user/project/merge_requests/merge_request_approvals.md
+++ b/doc/user/project/merge_requests/merge_request_approvals.md
@@ -229,12 +229,6 @@ The default approval settings can now be overridden when creating a
in the **No. approvals required** box.
1. Click **Update approvers**.
-There are however some restrictions:
-
-- The amount of required approvals, if changed, must be greater than the default
- set at the project level. This ensures that you're not forced to adjust settings
- when someone is unavailable for approval, yet the process is still enforced.
-
NOTE: **Note:**
If you are contributing to a forked project, things are a little different.
Read what happens when the
@@ -242,14 +236,9 @@ Read what happens when the
## Overriding merge request approvals default settings **(PREMIUM)**
-In GitLab Premium, when the approval rules are [set at the project level](#editing-approvals-premium), and
-**Can override approvers and approvals required per merge request** is checked, there are a few more
-restrictions (compared to [GitLab Starter](#overriding-the-merge-request-approvals-default-settings)):
-
-- Approval rules can be added to an MR with no restriction.
-- For project sourced approval rules, editing and removing approvers is not allowed.
-- The approvals required of all approval rules is configurable, but if a rule is backed by a project rule, then it is restricted
- to the minimum approvals required set in the project's corresponding rule.
+In GitLab Premium, when the approval rules are [set at the project level](#editing-approvals-premium),
+and **Can override approvers and approvals required per merge request** is checked,
+approval rules can be added to an MR with no restriction.
## Resetting approvals on push
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index 4e25d8545e9..f3e9c950efd 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -6,7 +6,9 @@ type: reference, howto
> - Introduced in GitLab 9.1 as [Trigger Schedule](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10533).
> - [Renamed to Pipeline Schedule](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10853) in GitLab 9.2.
-> - Cron notation is parsed by [Fugit](https://github.com/floraison/fugit).
+
+NOTE: **Note:**
+Cron notation is parsed by [Fugit](https://github.com/floraison/fugit).
Pipelines are normally run based on certain conditions being met. For example, when a branch is pushed to repository.
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/expand_variables.rb b/lib/expand_variables.rb
index c83cec9dc4a..45af30f46dc 100644
--- a/lib/expand_variables.rb
+++ b/lib/expand_variables.rb
@@ -3,6 +3,20 @@
module ExpandVariables
class << self
def expand(value, variables)
+ variables_hash = nil
+
+ value.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
+ variables_hash ||= transform_variables(variables)
+ variables_hash[$1 || $2]
+ end
+ end
+
+ private
+
+ def transform_variables(variables)
+ # Lazily initialise variables
+ variables = variables.call if variables.is_a?(Proc)
+
# Convert hash array to variables
if variables.is_a?(Array)
variables = variables.reduce({}) do |hash, variable|
@@ -11,9 +25,7 @@ module ExpandVariables
end
end
- value.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
- variables[$1 || $2]
- end
+ variables
end
end
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/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 2fd76bc3690..29a52b9da17 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -16,8 +16,11 @@ module Gitlab
dependencies needs before_script after_script variables
environment coverage retry parallel extends].freeze
+ REQUIRED_BY_NEEDS = %i[stage].freeze
+
validations do
validates :config, allowed_keys: ALLOWED_KEYS
+ validates :config, required_keys: REQUIRED_BY_NEEDS, if: :has_needs?
validates :config, presence: true
validates :script, presence: true
validates :name, presence: true
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/config/entry/attributable.rb b/lib/gitlab/config/entry/attributable.rb
index 560fe63df0e..87bd257f69a 100644
--- a/lib/gitlab/config/entry/attributable.rb
+++ b/lib/gitlab/config/entry/attributable.rb
@@ -18,6 +18,10 @@ module Gitlab
config[attribute]
end
+
+ define_method("has_#{attribute}?") do
+ config.is_a?(Hash) && config.key?(attribute)
+ end
end
end
end
diff --git a/lib/gitlab/config/entry/validators.rb b/lib/gitlab/config/entry/validators.rb
index 6796fcce75f..0289e675c6b 100644
--- a/lib/gitlab/config/entry/validators.rb
+++ b/lib/gitlab/config/entry/validators.rb
@@ -26,6 +26,17 @@ module Gitlab
end
end
+ class RequiredKeysValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ present_keys = options[:in] - value.try(:keys).to_a
+
+ if present_keys.any?
+ record.errors.add(attribute, "missing required keys: " +
+ present_keys.join(', '))
+ end
+ end
+ end
+
class AllowedValuesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless options[:in].include?(value.to_s)
diff --git a/lib/gitlab/git_post_receive.rb b/lib/gitlab/git_post_receive.rb
index d98b85fecc4..24d752b8a4b 100644
--- a/lib/gitlab/git_post_receive.rb
+++ b/lib/gitlab/git_post_receive.rb
@@ -27,6 +27,18 @@ module Gitlab
end
end
+ def includes_branches?
+ enum_for(:changes_refs).any? do |_oldrev, _newrev, ref|
+ Gitlab::Git.branch_ref?(ref)
+ end
+ end
+
+ def includes_tags?
+ enum_for(:changes_refs).any? do |_oldrev, _newrev, ref|
+ Gitlab::Git.tag_ref?(ref)
+ end
+ end
+
private
def deserialize_changes(changes)
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/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index 42c4745ff98..6e4286589c1 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -3,8 +3,8 @@
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.12.3'.freeze
- KUBECTL_VERSION = '1.11.7'.freeze
+ HELM_VERSION = '2.14.3'.freeze
+ KUBECTL_VERSION = '1.11.10'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
SERVICE_ACCOUNT = 'tiller'.freeze
CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
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/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index e83e736d430..038553c5dd7 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -143,7 +143,8 @@ module Gitlab
Gitlab::UsageDataCounters::NoteCounter,
Gitlab::UsageDataCounters::SnippetCounter,
Gitlab::UsageDataCounters::SearchCounter,
- Gitlab::UsageDataCounters::CycleAnalyticsCounter
+ Gitlab::UsageDataCounters::CycleAnalyticsCounter,
+ Gitlab::UsageDataCounters::SourceCodeCounter
]
end
diff --git a/lib/gitlab/usage_data_counters/source_code_counter.rb b/lib/gitlab/usage_data_counters/source_code_counter.rb
new file mode 100644
index 00000000000..8a1771a7bd1
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/source_code_counter.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Gitlab::UsageDataCounters
+ class SourceCodeCounter < BaseCounter
+ KNOWN_EVENTS = %w[pushes].freeze
+ PREFIX = 'source_code'
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b96ceb970ae..27435576495 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 ""
@@ -750,7 +750,7 @@ msgstr ""
msgid "Added %{label_references} %{label_text}."
msgstr ""
-msgid "Added a todo."
+msgid "Added a To Do."
msgstr ""
msgid "Added at"
@@ -765,7 +765,7 @@ msgstr ""
msgid "Adds %{labels} %{label_text}."
msgstr ""
-msgid "Adds a todo."
+msgid "Adds a To Do."
msgstr ""
msgid "Admin Area"
@@ -1362,9 +1362,6 @@ msgstr ""
msgid "Assign"
msgstr ""
-msgid "Assign command failed because no user was found"
-msgstr ""
-
msgid "Assign custom color like #FF0000"
msgstr ""
@@ -1948,6 +1945,9 @@ msgstr ""
msgid "CICD|instance enabled"
msgstr ""
+msgid "CLOSED"
+msgstr ""
+
msgid "CONTRIBUTING"
msgstr ""
@@ -2008,6 +2008,15 @@ msgstr ""
msgid "Certificate (PEM)"
msgstr ""
+msgid "Change Label"
+msgstr ""
+
+msgid "Change assignee"
+msgstr ""
+
+msgid "Change milestone"
+msgstr ""
+
msgid "Change path"
msgstr ""
@@ -2953,9 +2962,6 @@ msgstr ""
msgid "Commands applied"
msgstr ""
-msgid "Commands did not apply"
-msgstr ""
-
msgid "Comment"
msgstr ""
@@ -2988,6 +2994,9 @@ msgstr[1] ""
msgid "Commit %{commit_id}"
msgstr ""
+msgid "Commit (when editing commit message)"
+msgstr ""
+
msgid "Commit Message"
msgstr ""
@@ -3378,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"
@@ -3486,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"
@@ -3501,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."
@@ -4100,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 ""
@@ -4769,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 ""
@@ -4811,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 ""
@@ -4936,6 +4975,9 @@ msgstr ""
msgid "Filter results by project"
msgstr ""
+msgid "Filter your projects by name"
+msgstr ""
+
msgid "Filter..."
msgstr ""
@@ -4954,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 ""
@@ -4978,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 ""
@@ -5197,6 +5248,9 @@ msgstr ""
msgid "Given access %{time_ago}"
msgstr ""
+msgid "Global Shortcuts"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -5221,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 ""
@@ -6162,6 +6285,9 @@ msgstr ""
msgid "Key: %{key}"
msgstr ""
+msgid "Keyboard Shortcuts"
+msgstr ""
+
msgid "Kubernetes"
msgstr ""
@@ -6293,9 +6419,15 @@ msgstr ""
msgid "Last seen"
msgstr ""
+msgid "Last successful update"
+msgstr ""
+
msgid "Last update"
msgstr ""
+msgid "Last update attempt"
+msgstr ""
+
msgid "Last updated"
msgstr ""
@@ -6445,13 +6577,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."
@@ -6460,19 +6592,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."
@@ -6481,7 +6616,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"
@@ -6547,10 +6682,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"
@@ -6562,9 +6697,6 @@ msgstr ""
msgid "Mark this issue as a duplicate of another issue"
msgstr ""
-msgid "Mark to do as done"
-msgstr ""
-
msgid "Markdown"
msgstr ""
@@ -6577,13 +6709,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."
@@ -6592,9 +6727,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 ""
@@ -6955,18 +7087,24 @@ 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 ""
@@ -7030,6 +7168,9 @@ msgstr ""
msgid "Network"
msgstr ""
+msgid "Network Graph"
+msgstr ""
+
msgid "Never"
msgstr ""
@@ -7490,6 +7631,9 @@ msgstr ""
msgid "Open Documentation"
msgstr ""
+msgid "Open Selection"
+msgstr ""
+
msgid "Open comment type dropdown"
msgstr ""
@@ -8495,6 +8639,12 @@ msgstr ""
msgid "Project Badges"
msgstr ""
+msgid "Project File"
+msgstr ""
+
+msgid "Project Files browsing"
+msgstr ""
+
msgid "Project ID"
msgstr ""
@@ -9247,6 +9397,9 @@ msgstr ""
msgid "Replaced all labels with %{label_references} %{label_text}."
msgstr ""
+msgid "Reply (quoting selected text)"
+msgstr ""
+
msgid "Reply by email"
msgstr ""
@@ -9609,15 +9762,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 ""
@@ -10062,6 +10227,9 @@ msgstr ""
msgid "Show whitespace changes"
msgstr ""
+msgid "Show/hide this dialog"
+msgstr ""
+
msgid "Showing %d event"
msgid_plural "Showing %d events"
msgstr[0] ""
@@ -11777,6 +11945,9 @@ msgstr ""
msgid "Today"
msgstr ""
+msgid "Toggle Markdown preview"
+msgstr ""
+
msgid "Toggle Sidebar"
msgstr ""
@@ -11801,6 +11972,9 @@ msgstr ""
msgid "Toggle sidebar"
msgstr ""
+msgid "Toggle the Performance Bar"
+msgstr ""
+
msgid "Toggle thread"
msgstr ""
@@ -12020,10 +12194,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."
@@ -12562,6 +12736,9 @@ msgstr ""
msgid "Wiki"
msgstr ""
+msgid "Wiki pages"
+msgstr ""
+
msgid "Wiki was successfully updated."
msgstr ""
@@ -13777,6 +13954,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/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb
index 84bbe0525e5..c3a5abf2b7a 100644
--- a/spec/bin/changelog_spec.rb
+++ b/spec/bin/changelog_spec.rb
@@ -15,7 +15,7 @@ describe 'bin/changelog' do
allow(entry).to receive(:branch_name).and_return('long-branch-' * 100)
file_path = entry.send(:file_path)
- expect(file_path.length).to eq(140)
+ expect(file_path.length).to eq(99)
end
end
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/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 8ee3168273f..b958f419a19 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -60,7 +60,7 @@ describe Projects::RawController do
execute_raw_requests(requests: 6, project: project, file_path: file_path)
expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.')
- expect(response).to redirect_to(project_blob_path(project, file_path))
+ expect(response).to have_gitlab_http_status(429)
end
it 'logs the event on auth.log' do
@@ -92,7 +92,7 @@ describe Projects::RawController do
execute_raw_requests(requests: 3, project: project, file_path: modified_path)
expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.')
- expect(response).to redirect_to(project_blob_path(project, modified_path))
+ expect(response).to have_gitlab_http_status(429)
end
end
@@ -120,7 +120,7 @@ describe Projects::RawController do
execute_raw_requests(requests: 6, project: project, file_path: file_path)
expect(flash[:alert]).to eq('You cannot access the raw file. Please wait a minute.')
- expect(response).to redirect_to(project_blob_path(project, file_path))
+ expect(response).to have_gitlab_http_status(429)
# Accessing upcase version of readme
file_path = "#{commit_sha}/README.md"
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/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb
new file mode 100644
index 00000000000..6d587053b4f
--- /dev/null
+++ b/spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Projects > Raw > User interacts with raw endpoint' do
+ include RepoHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository, :public) }
+ let(:file_path) { 'master/README.md' }
+
+ before do
+ stub_application_setting(raw_blob_request_limit: 3)
+ project.add_developer(user)
+ create_file_in_repo(project, 'master', 'master', 'README.md', 'readme content')
+
+ sign_in(user)
+ end
+
+ context 'when user access a raw file' do
+ it 'renders the page successfully' do
+ visit project_raw_url(project, file_path)
+
+ expect(source).to eq('') # Body is filled in by gitlab-workhorse
+ end
+ end
+
+ context 'when user goes over the rate requests limit' do
+ it 'returns too many requests' do
+ 4.times do
+ visit project_raw_url(project, file_path)
+ end
+
+ expect(source).to have_content('You are being redirected')
+ click_link('redirected')
+ expect(page).to have_content('You cannot access the raw file. Please wait a minute.')
+ end
+ end
+end
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/javascripts/environments/confirm_rollback_modal_spec.js b/spec/frontend/environments/confirm_rollback_modal_spec.js
index 05715bce38f..a1a22274e8f 100644
--- a/spec/javascripts/environments/confirm_rollback_modal_spec.js
+++ b/spec/frontend/environments/confirm_rollback_modal_spec.js
@@ -61,7 +61,7 @@ describe('Confirm Rollback Modal Component', () => {
environment,
},
});
- const eventHubSpy = spyOn(eventHub, '$emit');
+ const eventHubSpy = jest.spyOn(eventHub, '$emit');
const modal = component.find(GlModal);
modal.vm.$emit('ok');
diff --git a/spec/javascripts/environments/environment_rollback_spec.js b/spec/frontend/environments/environment_rollback_spec.js
index 8c47f6a12c0..fb62a096c3d 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js
+++ b/spec/frontend/environments/environment_rollback_spec.js
@@ -1,46 +1,38 @@
-import Vue from 'vue';
-import { shallowMount } from '@vue/test-utils';
+import { shallowMount, mount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import eventHub from '~/environments/event_hub';
-import rollbackComp from '~/environments/components/environment_rollback.vue';
+import RollbackComponent from '~/environments/components/environment_rollback.vue';
describe('Rollback Component', () => {
const retryUrl = 'https://gitlab.com/retry';
- let RollbackComponent;
-
- beforeEach(() => {
- RollbackComponent = Vue.extend(rollbackComp);
- });
it('Should render Re-deploy label when isLastDeployment is true', () => {
- const component = new RollbackComponent({
- el: document.querySelector('.test-dom-element'),
+ const wrapper = mount(RollbackComponent, {
propsData: {
retryUrl,
isLastDeployment: true,
environment: {},
},
- }).$mount();
+ });
- expect(component.$el).toHaveSpriteIcon('repeat');
+ expect(wrapper.element).toHaveSpriteIcon('repeat');
});
it('Should render Rollback label when isLastDeployment is false', () => {
- const component = new RollbackComponent({
- el: document.querySelector('.test-dom-element'),
+ const wrapper = mount(RollbackComponent, {
propsData: {
retryUrl,
isLastDeployment: false,
environment: {},
},
- }).$mount();
+ });
- expect(component.$el).toHaveSpriteIcon('redo');
+ expect(wrapper.element).toHaveSpriteIcon('redo');
});
it('should emit a "rollback" event on button click', () => {
- const eventHubSpy = spyOn(eventHub, '$emit');
- const component = shallowMount(RollbackComponent, {
+ const eventHubSpy = jest.spyOn(eventHub, '$emit');
+ const wrapper = shallowMount(RollbackComponent, {
propsData: {
retryUrl,
environment: {
@@ -48,7 +40,7 @@ describe('Rollback Component', () => {
},
},
});
- const button = component.find(GlButton);
+ const button = wrapper.find(GlButton);
button.vm.$emit('click');
diff --git a/spec/frontend/matchers.js b/spec/frontend/matchers.js
new file mode 100644
index 00000000000..35c362d0bf5
--- /dev/null
+++ b/spec/frontend/matchers.js
@@ -0,0 +1,38 @@
+export default {
+ toHaveSpriteIcon: (element, iconName) => {
+ if (!iconName) {
+ throw new Error('toHaveSpriteIcon is missing iconName argument!');
+ }
+
+ if (!(element instanceof HTMLElement)) {
+ throw new Error(`${element} is not a DOM element!`);
+ }
+
+ const iconReferences = [].slice.apply(element.querySelectorAll('svg use'));
+ const matchingIcon = iconReferences.find(reference =>
+ reference.getAttribute('xlink:href').endsWith(`#${iconName}`),
+ );
+
+ const pass = Boolean(matchingIcon);
+
+ let message;
+ if (pass) {
+ message = `${element.outerHTML} contains the sprite icon "${iconName}"!`;
+ } else {
+ message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`;
+
+ const existingIcons = iconReferences.map(reference => {
+ const iconUrl = reference.getAttribute('xlink:href');
+ return `"${iconUrl.replace(/^.+#/, '')}"`;
+ });
+ if (existingIcons.length > 0) {
+ message += ` (only found ${existingIcons.join(',')})`;
+ }
+ }
+
+ return {
+ pass,
+ message: () => message,
+ };
+ },
+};
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/frontend/test_setup.js b/spec/frontend/test_setup.js
index 8b6f7802b15..df8a625319b 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -6,6 +6,7 @@ import { config as testUtilsConfig } from '@vue/test-utils';
import { initializeTestTimeout } from './helpers/timeout';
import { loadHTMLFixture, setHTMLFixture } from './helpers/fixtures';
import { setupManualMocks } from './mocks/mocks_helper';
+import customMatchers from './matchers';
// Expose jQuery so specs using jQuery plugins can be imported nicely.
// Here is an issue to explore better alternatives:
@@ -67,6 +68,8 @@ Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => {
});
});
+expect.extend(customMatchers);
+
// Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false;
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/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/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/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb
index 099d7b6b67c..394efa85701 100644
--- a/spec/lib/expand_variables_spec.rb
+++ b/spec/lib/expand_variables_spec.rb
@@ -4,62 +4,131 @@ require 'spec_helper'
describe ExpandVariables do
describe '#expand' do
- subject { described_class.expand(value, variables) }
+ context 'table tests' do
+ using RSpec::Parameterized::TableSyntax
- tests = [
- { value: 'key',
- result: 'key',
- variables: [] },
- { value: 'key$variable',
- result: 'key',
- variables: [] },
- { value: 'key$variable',
- result: 'keyvalue',
- variables: [
- { key: 'variable', value: 'value' }
- ] },
- { value: 'key${variable}',
- result: 'keyvalue',
- variables: [
- { key: 'variable', value: 'value' }
- ] },
- { value: 'key$variable$variable2',
- result: 'keyvalueresult',
- variables: [
- { key: 'variable', value: 'value' },
- { key: 'variable2', value: 'result' }
- ] },
- { value: 'key${variable}${variable2}',
- result: 'keyvalueresult',
- variables: [
- { key: 'variable', value: 'value' },
- { key: 'variable2', value: 'result' }
- ] },
- { value: 'key$variable2$variable',
- result: 'keyresultvalue',
- variables: [
- { key: 'variable', value: 'value' },
- { key: 'variable2', value: 'result' }
- ] },
- { value: 'key${variable2}${variable}',
- result: 'keyresultvalue',
- variables: [
- { key: 'variable', value: 'value' },
- { key: 'variable2', value: 'result' }
- ] },
- { value: 'review/$CI_COMMIT_REF_NAME',
- result: 'review/feature/add-review-apps',
- variables: [
- { key: 'CI_COMMIT_REF_NAME', value: 'feature/add-review-apps' }
- ] }
- ]
+ where do
+ {
+ "no expansion": {
+ value: 'key',
+ result: 'key',
+ variables: []
+ },
+ "missing variable": {
+ value: 'key$variable',
+ result: 'key',
+ variables: []
+ },
+ "simple expansion": {
+ value: 'key$variable',
+ result: 'keyvalue',
+ variables: [
+ { key: 'variable', value: 'value' }
+ ]
+ },
+ "simple with hash of variables": {
+ value: 'key$variable',
+ result: 'keyvalue',
+ variables: {
+ 'variable' => 'value'
+ }
+ },
+ "complex expansion": {
+ value: 'key${variable}',
+ result: 'keyvalue',
+ variables: [
+ { key: 'variable', value: 'value' }
+ ]
+ },
+ "simple expansions": {
+ value: 'key$variable$variable2',
+ result: 'keyvalueresult',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ "complex expansions": {
+ value: 'key${variable}${variable2}',
+ result: 'keyvalueresult',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ "complex expansions with missing variable": {
+ value: 'key${variable}${variable2}',
+ result: 'keyvalue',
+ variables: [
+ { key: 'variable', value: 'value' }
+ ]
+ },
+ "out-of-order expansion": {
+ value: 'key$variable2$variable',
+ result: 'keyresultvalue',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ "out-of-order complex expansion": {
+ value: 'key${variable2}${variable}',
+ result: 'keyresultvalue',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ "review-apps expansion": {
+ value: 'review/$CI_COMMIT_REF_NAME',
+ result: 'review/feature/add-review-apps',
+ variables: [
+ { key: 'CI_COMMIT_REF_NAME', value: 'feature/add-review-apps' }
+ ]
+ },
+ "do not lazily access variables when no expansion": {
+ value: 'key',
+ result: 'key',
+ variables: -> { raise NotImplementedError }
+ },
+ "lazily access variables": {
+ value: 'key$variable',
+ result: 'keyvalue',
+ variables: -> { [{ key: 'variable', value: 'value' }] }
+ }
+ }
+ end
+
+ with_them do
+ subject { ExpandVariables.expand(value, variables) } # rubocop:disable RSpec/DescribedClass
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ context 'lazily inits variables' do
+ let(:variables) { -> { [{ key: 'variable', value: 'result' }] } }
+
+ subject { described_class.expand(value, variables) }
+
+ context 'when expanding variable' do
+ let(:value) { 'key$variable$variable2' }
+
+ it 'calls block at most once' do
+ expect(variables).to receive(:call).once.and_call_original
+
+ is_expected.to eq('keyresult')
+ end
+ end
+
+ context 'when no expansion is needed' do
+ let(:value) { 'key' }
- tests.each do |test|
- context "#{test[:value]} resolves to #{test[:result]}" do
- let(:value) { test[:value] }
- let(:variables) { test[:variables] }
+ it 'does not call block' do
+ expect(variables).not_to receive(:call)
- it { is_expected.to eq(test[:result]) }
+ is_expected.to eq('key')
+ end
end
end
end
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/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 800ef122203..415ade7a096 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -89,14 +89,23 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when has needs' do
let(:config) do
- { script: 'echo', needs: ['another-job'] }
+ {
+ stage: 'test',
+ script: 'echo',
+ needs: ['another-job']
+ }
end
it { expect(entry).to be_valid }
context 'when has dependencies' do
let(:config) do
- { script: 'echo', dependencies: ['another-job'], needs: ['another-job'] }
+ {
+ stage: 'test',
+ script: 'echo',
+ dependencies: ['another-job'],
+ needs: ['another-job']
+ }
end
it { expect(entry).to be_valid }
@@ -256,7 +265,11 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when has needs' do
context 'that are not a array of strings' do
let(:config) do
- { script: 'echo', needs: 'build-job' }
+ {
+ stage: 'test',
+ script: 'echo',
+ needs: 'build-job'
+ }
end
it 'returns error about invalid type' do
@@ -267,7 +280,12 @@ describe Gitlab::Ci::Config::Entry::Job do
context 'when have dependencies that are not subset of needs' do
let(:config) do
- { script: 'echo', dependencies: ['another-job'], needs: ['build-job'] }
+ {
+ stage: 'test',
+ script: 'echo',
+ dependencies: ['another-job'],
+ needs: ['build-job']
+ }
end
it 'returns error about invalid data' do
@@ -275,6 +293,20 @@ describe Gitlab::Ci::Config::Entry::Job do
expect(entry.errors).to include 'job dependencies the another-job should be part of needs'
end
end
+
+ context 'when stage: is missing' do
+ let(:config) do
+ {
+ script: 'echo',
+ needs: ['build-job']
+ }
+ end
+
+ it 'returns error about invalid data' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include 'job config missing required keys: stage'
+ end
+ end
end
end
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/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb
index 82614bb8238..6b548d5c4a8 100644
--- a/spec/lib/gitlab/config/entry/attributable_spec.rb
+++ b/spec/lib/gitlab/config/entry/attributable_spec.rb
@@ -25,7 +25,9 @@ describe Gitlab::Config::Entry::Attributable do
end
it 'returns the value of config' do
+ expect(instance).to have_name
expect(instance.name).to eq 'some name'
+ expect(instance).to have_test
expect(instance.test).to eq 'some test'
end
@@ -42,6 +44,7 @@ describe Gitlab::Config::Entry::Attributable do
end
it 'returns nil' do
+ expect(instance).not_to have_test
expect(instance.test).to be_nil
end
end
diff --git a/spec/lib/gitlab/git_post_receive_spec.rb b/spec/lib/gitlab/git_post_receive_spec.rb
new file mode 100644
index 00000000000..4c20d945585
--- /dev/null
+++ b/spec/lib/gitlab/git_post_receive_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Gitlab::GitPostReceive do
+ let(:project) { create(:project) }
+
+ subject { described_class.new(project, "project-#{project.id}", changes.dup, {}) }
+
+ describe '#includes_branches?' do
+ context 'with no branches' do
+ let(:changes) do
+ <<~EOF
+ 654321 210987 refs/nobranches/tag1
+ 654322 210986 refs/tags/test1
+ 654323 210985 refs/merge-requests/mr1
+ EOF
+ end
+
+ it 'returns false' do
+ expect(subject.includes_branches?).to be_falsey
+ end
+ end
+
+ context 'with branches' do
+ let(:changes) do
+ <<~EOF
+ 654322 210986 refs/heads/test1
+ 654321 210987 refs/tags/tag1
+ 654323 210985 refs/merge-requests/mr1
+ EOF
+ end
+
+ it 'returns true' do
+ expect(subject.includes_branches?).to be_truthy
+ end
+ end
+
+ context 'with malformed changes' do
+ let(:changes) do
+ <<~EOF
+ ref/heads/1 a
+ somebranch refs/heads/2
+ EOF
+ end
+
+ it 'returns false' do
+ expect(subject.includes_branches?).to be_falsey
+ end
+ end
+ end
+
+ describe '#includes_tags?' do
+ context 'with no tags' do
+ let(:changes) do
+ <<~EOF
+ 654321 210987 refs/notags/tag1
+ 654322 210986 refs/heads/test1
+ 654323 210985 refs/merge-requests/mr1
+ EOF
+ end
+
+ it 'returns false' do
+ expect(subject.includes_tags?).to be_falsey
+ end
+ end
+
+ context 'with tags' do
+ let(:changes) do
+ <<~EOF
+ 654322 210986 refs/heads/test1
+ 654321 210987 refs/tags/tag1
+ 654323 210985 refs/merge-requests/mr1
+ EOF
+ end
+
+ it 'returns true' do
+ expect(subject.includes_tags?).to be_truthy
+ end
+ end
+
+ context 'with malformed changes' do
+ let(:changes) do
+ <<~EOF
+ ref/tags/1 a
+ sometag refs/tags/2
+ EOF
+ end
+
+ it 'returns false' do
+ expect(subject.includes_tags?).to be_falsey
+ end
+ end
+ end
+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/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 06c8d127951..fce2aded786 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'generates the appropriate specifications for the container' do
container = subject.generate.spec.containers.first
expect(container.name).to eq('helm')
- expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.3-kube-1.11.7')
+ expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.14.3-kube-1.11.10')
expect(container.env.count).to eq(3)
expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
expect(container.command).to match_array(["/bin/sh"])
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/lib/gitlab/usage_data_counters/source_code_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/source_code_counter_spec.rb
new file mode 100644
index 00000000000..47077345e0c
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/source_code_counter_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::UsageDataCounters::SourceCodeCounter do
+ it_behaves_like 'a redis usage counter', 'Source Code', :pushes
+
+ it_behaves_like 'a redis usage counter with totals', :source_code, pushes: 5
+end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index a8d68b9e46b..588c68d1fb0 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -73,7 +73,8 @@ describe Gitlab::UsageData do
web_ide_commits: a_kind_of(Integer),
web_ide_merge_requests: a_kind_of(Integer),
navbar_searches: a_kind_of(Integer),
- cycle_analytics_views: a_kind_of(Integer)
+ cycle_analytics_views: a_kind_of(Integer),
+ source_code_pushes: a_kind_of(Integer)
)
end
diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb
index d4f8b552088..00b5c72a3d3 100644
--- a/spec/models/clusters/applications/helm_spec.rb
+++ b/spec/models/clusters/applications/helm_spec.rb
@@ -23,7 +23,7 @@ describe Clusters::Applications::Helm do
Clusters::Cluster::APPLICATIONS.keys.each do |application_name|
next if application_name == 'helm'
- it do
+ it "is false when #{application_name} is installed" do
cluster_application = create("clusters_applications_#{application_name}".to_sym)
helm = cluster_application.cluster.application_helm
@@ -31,6 +31,14 @@ describe Clusters::Applications::Helm do
expect(helm.allowed_to_uninstall?).to be_falsy
end
end
+
+ it 'executes a single query only' do
+ cluster_application = create(:clusters_applications_ingress)
+ helm = cluster_application.cluster.application_helm
+
+ query_count = ActiveRecord::QueryRecorder.new { helm.allowed_to_uninstall? }.count
+ expect(query_count).to eq(1)
+ end
end
context "without other existing applications" do
diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb
index 619ab96af94..cf7c7bf7e61 100644
--- a/spec/models/project_services/chat_message/pipeline_message_spec.rb
+++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb
@@ -42,9 +42,9 @@ describe ChatMessage::PipelineMessage do
before do
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
- test_project = double("A test project",
- commit_by: test_commit, name: args[:project][:name],
- web_url: args[:project][:web_url], avatar_url: args[:project][:avatar_url])
+ test_project = double("A test project", commit_by: test_commit, name: args[:project][:name], web_url: args[:project][:web_url])
+ allow(test_project).to receive(:avatar_url).with(no_args).and_return("/avatar")
+ allow(test_project).to receive(:avatar_url).with(only_path: false).and_return(args[:project][:avatar_url])
allow(Project).to receive(:find) { test_project }
test_pipeline = double("A test pipeline", has_yaml_errors?: has_yaml_errors,
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 29a589eba20..83ce58235dd 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2252,6 +2252,21 @@ describe Project do
end
end
+ describe '#mark_stuck_remote_mirrors_as_failed!' do
+ it 'fails stuck remote mirrors' do
+ project = create(:project, :repository, :remote_mirror)
+
+ project.remote_mirrors.first.update(
+ update_status: :started,
+ last_update_started_at: 2.days.ago
+ )
+
+ expect do
+ project.mark_stuck_remote_mirrors_as_failed!
+ end.to change { project.remote_mirrors.stuck.count }.from(1).to(0)
+ end
+ end
+
describe '#ancestors_upto' do
let(:parent) { create(:group) }
let(:child) { create(:group, parent: parent) }
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 687b0935c55..7edeb56efe2 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -153,14 +153,14 @@ describe RemoteMirror, :mailer do
end
end
- describe '#mark_as_failed' do
+ describe '#mark_as_failed!' do
let(:remote_mirror) { create(:remote_mirror) }
let(:error_message) { 'http://user:pass@test.com/root/repoC.git/' }
let(:sanitized_error_message) { 'http://*****:*****@test.com/root/repoC.git/' }
subject do
remote_mirror.update_start
- remote_mirror.mark_as_failed(error_message)
+ remote_mirror.mark_as_failed!(error_message)
end
it 'sets the update_status to failed' do
@@ -204,8 +204,8 @@ describe RemoteMirror, :mailer do
it 'includes mirrors that were started over an hour ago' do
mirror = create_mirror(url: 'http://cantbeblank',
update_status: 'started',
- last_update_at: 3.hours.ago,
- updated_at: 2.hours.ago)
+ last_update_started_at: 3.hours.ago,
+ last_update_at: 2.hours.ago)
expect(described_class.stuck.last).to eq(mirror)
end
@@ -214,7 +214,7 @@ describe RemoteMirror, :mailer do
mirror = create_mirror(url: 'http://cantbeblank',
update_status: 'started',
last_update_at: nil,
- updated_at: 4.hours.ago)
+ last_update_started_at: 4.hours.ago)
expect(described_class.stuck.last).to eq(mirror)
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 12dff440ce2..e68de2e73a8 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1744,12 +1744,23 @@ describe Repository do
end
end
- describe '#before_push_tag' do
+ describe '#expires_caches_for_tags' do
it 'flushes the cache' do
expect(repository).to receive(:expire_statistics_caches)
expect(repository).to receive(:expire_emptiness_caches)
expect(repository).to receive(:expire_tags_cache)
+ repository.expire_caches_for_tags
+ end
+ end
+
+ describe '#before_push_tag' do
+ it 'logs an event' do
+ expect(repository).not_to receive(:expire_statistics_caches)
+ expect(repository).not_to receive(:expire_emptiness_caches)
+ expect(repository).not_to receive(:expire_tags_cache)
+ expect(repository).to receive(:repository_event).with(:push_tag)
+
repository.before_push_tag
end
end
@@ -1781,6 +1792,12 @@ describe Repository do
repository.after_create_branch
end
+
+ it 'does not expire the branch caches when specified' do
+ expect(repository).not_to receive(:expire_branches_cache)
+
+ repository.after_create_branch(expire_cache: false)
+ end
end
describe '#after_remove_branch' do
@@ -1789,6 +1806,12 @@ describe Repository do
repository.after_remove_branch
end
+
+ it 'does not expire the branch caches when specified' do
+ expect(repository).not_to receive(:expire_branches_cache)
+
+ repository.after_remove_branch(expire_cache: false)
+ end
end
describe '#after_create' do
diff --git a/spec/policies/clusters/instance_policy_spec.rb b/spec/policies/clusters/instance_policy_spec.rb
index 7b61819e079..2373fef8aa6 100644
--- a/spec/policies/clusters/instance_policy_spec.rb
+++ b/spec/policies/clusters/instance_policy_spec.rb
@@ -9,6 +9,8 @@ describe Clusters::InstancePolicy do
describe 'rules' do
context 'when user' do
it { expect(policy).to be_disallowed :read_cluster }
+ it { expect(policy).to be_disallowed :add_cluster }
+ it { expect(policy).to be_disallowed :create_cluster }
it { expect(policy).to be_disallowed :update_cluster }
it { expect(policy).to be_disallowed :admin_cluster }
end
@@ -17,6 +19,8 @@ describe Clusters::InstancePolicy do
let(:user) { create(:admin) }
it { expect(policy).to be_allowed :read_cluster }
+ it { expect(policy).to be_allowed :add_cluster }
+ it { expect(policy).to be_allowed :create_cluster }
it { expect(policy).to be_allowed :update_cluster }
it { expect(policy).to be_allowed :admin_cluster }
end
diff --git a/spec/services/git/tag_push_service_spec.rb b/spec/services/git/tag_push_service_spec.rb
index 418952b52da..7e008637182 100644
--- a/spec/services/git/tag_push_service_spec.rb
+++ b/spec/services/git/tag_push_service_spec.rb
@@ -26,8 +26,8 @@ describe Git::TagPushService do
subject
end
- it 'flushes the tags cache' do
- expect(project.repository).to receive(:expire_tags_cache)
+ it 'does not flush the tags cache' do
+ expect(project.repository).not_to receive(:expire_tags_cache)
subject
end
diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb
index be2811ab1e7..4396ccab584 100644
--- a/spec/services/projects/update_remote_mirror_service_spec.rb
+++ b/spec/services/projects/update_remote_mirror_service_spec.rb
@@ -10,49 +10,91 @@ describe Projects::UpdateRemoteMirrorService do
subject(:service) { described_class.new(project, project.creator) }
- describe "#execute" do
+ describe '#execute' do
+ subject(:execute!) { service.execute(remote_mirror, 0) }
+
before do
project.repository.add_branch(project.owner, 'existing-branch', 'master')
allow(remote_mirror).to receive(:update_repository).and_return(true)
end
- it "ensures the remote exists" do
+ it 'ensures the remote exists' do
stub_fetch_remote(project, remote_name: remote_name, ssh_auth: remote_mirror)
expect(remote_mirror).to receive(:ensure_remote!)
- service.execute(remote_mirror)
+ execute!
end
- it "fetches the remote repository" do
+ it 'fetches the remote repository' do
expect(project.repository)
.to receive(:fetch_remote)
- .with(remote_mirror.remote_name, no_tags: true, ssh_auth: remote_mirror)
+ .with(remote_mirror.remote_name, no_tags: true, ssh_auth: remote_mirror)
- service.execute(remote_mirror)
+ execute!
end
- it "returns success when updated succeeds" do
+ it 'marks the mirror as started when beginning' do
+ expect(remote_mirror).to receive(:update_start!).and_call_original
+
+ execute!
+ end
+
+ it 'marks the mirror as successfully finished' do
stub_fetch_remote(project, remote_name: remote_name, ssh_auth: remote_mirror)
- result = service.execute(remote_mirror)
+ result = execute!
expect(result[:status]).to eq(:success)
+ expect(remote_mirror).to be_finished
+ end
+
+ it 'marks the mirror as failed and raises the error when an unexpected error occurs' do
+ allow(project.repository).to receive(:fetch_remote).and_raise('Badly broken')
+
+ expect { execute! }.to raise_error /Badly broken/
+
+ expect(remote_mirror).to be_failed
+ expect(remote_mirror.last_error).to include('Badly broken')
+ end
+
+ context 'when the update fails because of a `Gitlab::Git::CommandError`' do
+ before do
+ allow(project.repository).to receive(:fetch_remote).and_raise(Gitlab::Git::CommandError.new('fetch failed'))
+ end
+
+ it 'wraps `Gitlab::Git::CommandError`s in a service error' do
+ expect(execute!).to eq(status: :error, message: 'fetch failed')
+ end
+
+ it 'marks the mirror as to be retried' do
+ execute!
+
+ expect(remote_mirror).to be_to_retry
+ expect(remote_mirror.last_error).to include('fetch failed')
+ end
+
+ it "marks the mirror as failed after #{described_class::MAX_TRIES} tries" do
+ service.execute(remote_mirror, described_class::MAX_TRIES)
+
+ expect(remote_mirror).to be_failed
+ expect(remote_mirror.last_error).to include('fetch failed')
+ end
end
context 'when syncing all branches' do
- it "push all the branches the first time" do
+ it 'push all the branches the first time' do
stub_fetch_remote(project, remote_name: remote_name, ssh_auth: remote_mirror)
expect(remote_mirror).to receive(:update_repository).with({})
- service.execute(remote_mirror)
+ execute!
end
end
context 'when only syncing protected branches' do
- it "sync updated protected branches" do
+ it 'sync updated protected branches' do
stub_fetch_remote(project, remote_name: remote_name, ssh_auth: remote_mirror)
protected_branch = create_protected_branch(project)
remote_mirror.only_protected_branches = true
@@ -61,7 +103,7 @@ describe Projects::UpdateRemoteMirrorService do
.to receive(:update_repository)
.with(only_branches_matching: [protected_branch.name])
- service.execute(remote_mirror)
+ execute!
end
def create_protected_branch(project)
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/policies/clusterable_shared_examples.rb b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
index 4f9873d53e4..0b427c23256 100644
--- a/spec/support/shared_examples/policies/clusterable_shared_examples.rb
+++ b/spec/support/shared_examples/policies/clusterable_shared_examples.rb
@@ -13,7 +13,11 @@ shared_examples 'clusterable policies' do
clusterable.add_developer(current_user)
end
+ it { expect_disallowed(:read_cluster) }
it { expect_disallowed(:add_cluster) }
+ it { expect_disallowed(:create_cluster) }
+ it { expect_disallowed(:update_cluster) }
+ it { expect_disallowed(:admin_cluster) }
end
context 'with a maintainer' do
@@ -22,7 +26,11 @@ shared_examples 'clusterable policies' do
end
context 'with no clusters' do
+ it { expect_allowed(:read_cluster) }
it { expect_allowed(:add_cluster) }
+ it { expect_allowed(:create_cluster) }
+ it { expect_allowed(:update_cluster) }
+ it { expect_allowed(:admin_cluster) }
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/spec/workers/build_process_worker_spec.rb b/spec/workers/build_process_worker_spec.rb
deleted file mode 100644
index d9a02ece142..00000000000
--- a/spec/workers/build_process_worker_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe BuildProcessWorker do
- describe '#perform' do
- context 'when build exists' do
- let(:pipeline) { create(:ci_pipeline) }
- let(:build) { create(:ci_build, pipeline: pipeline) }
-
- it 'processes build' do
- expect_any_instance_of(Ci::Pipeline).to receive(:process!)
- .with([build.id])
-
- described_class.new.perform(build.id)
- end
- end
-
- context 'when build does not exist' do
- it 'does not raise exception' do
- expect { described_class.new.perform(123) }
- .not_to raise_error
- end
- end
- end
-end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 39f1beb4efa..4c9fc0e3a24 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -14,6 +14,10 @@ describe PostReceive do
create(:project, :repository, auto_cancel_pending_pipelines: 'disabled')
end
+ def perform(changes: base64_changes)
+ described_class.new.perform(gl_repository, key_id, changes)
+ end
+
context "as a sidekiq worker" do
it "responds to #perform" do
expect(described_class.new).to respond_to(:perform)
@@ -28,7 +32,7 @@ describe PostReceive do
it "returns false and logs an error" do
expect(Gitlab::GitLogger).to receive(:error).with("POST-RECEIVE: #{error_message}")
- expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be(false)
+ expect(perform).to be(false)
end
end
@@ -39,7 +43,7 @@ describe PostReceive do
expect(Git::TagPushService).not_to receive(:new)
expect_next_instance_of(SystemHooksService) { |service| expect(service).to receive(:execute_hooks) }
- described_class.new.perform(gl_repository, key_id, "")
+ perform(changes: "")
end
end
@@ -50,40 +54,80 @@ describe PostReceive do
expect(Git::BranchPushService).not_to receive(:new)
expect(Git::TagPushService).not_to receive(:new)
- expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be false
+ expect(perform).to be false
end
end
context 'with changes' do
before do
allow_any_instance_of(Gitlab::GitPostReceive).to receive(:identify).and_return(project.owner)
+ allow(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::PROJECT])
end
context "branches" do
- let(:changes) { "123456 789012 refs/heads/tést" }
+ let(:changes) do
+ <<~EOF
+ '123456 789012 refs/heads/tést1'
+ '123456 789012 refs/heads/tést2'
+ EOF
+ end
- it "calls Git::BranchPushService" do
- expect_next_instance_of(Git::BranchPushService) do |service|
+ it 'expires the branches cache' do
+ expect(project.repository).to receive(:expire_branches_cache).once
+
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
+
+ it 'calls Git::BranchPushService' do
+ expect_any_instance_of(Git::BranchPushService) do |service|
expect(service).to receive(:execute).and_return(true)
end
expect(Git::TagPushService).not_to receive(:new)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
end
end
context "tags" do
- let(:changes) { "123456 789012 refs/tags/tag" }
+ let(:changes) do
+ <<~EOF
+ 654321 210987 refs/tags/tag1
+ 654322 210986 refs/tags/tag2
+ 654323 210985 refs/tags/tag3
+ 654324 210984 refs/tags/tag4
+ 654325 210983 refs/tags/tag5
+ EOF
+ end
+
+ before do
+ expect(Gitlab::GlRepository).to receive(:parse).and_return([project, Gitlab::GlRepository::PROJECT])
+ end
+
+ it 'does not expire branches cache' do
+ expect(project.repository).not_to receive(:expire_branches_cache)
+
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
+
+ it "only invalidates tags once" do
+ expect(project.repository).to receive(:repository_event).exactly(5).times.with(:push_tag).and_call_original
+ expect(project.repository).to receive(:expire_caches_for_tags).once.and_call_original
+ expect(project.repository).to receive(:expire_tags_cache).once.and_call_original
+
+ described_class.new.perform(gl_repository, key_id, base64_changes)
+ end
it "calls Git::TagPushService" do
- expect(Git::BranchPushService).not_to receive(:execute)
+ expect(Git::BranchPushService).not_to receive(:new)
- expect_next_instance_of(Git::TagPushService) do |service|
+ expect_any_instance_of(Git::TagPushService) do |service|
expect(service).to receive(:execute).and_return(true)
end
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ expect(Git::BranchPushService).not_to receive(:new)
+
+ perform
end
end
@@ -94,7 +138,7 @@ describe PostReceive do
expect(Git::BranchPushService).not_to receive(:new)
expect(Git::TagPushService).not_to receive(:new)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
end
end
@@ -111,7 +155,7 @@ describe PostReceive do
let(:changes_count) { changes.lines.count }
- subject { described_class.new.perform(gl_repository, key_id, base64_changes) }
+ subject { perform }
context "with valid .gitlab-ci.yml" do
before do
@@ -180,7 +224,13 @@ describe PostReceive do
it 'calls SystemHooksService' do
expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(fake_hook_data, :repository_update_hooks).and_return(true)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
+ end
+
+ it 'increments the usage data counter of pushes event' do
+ counter = Gitlab::UsageDataCounters::SourceCodeCounter
+
+ expect { perform }.to change { counter.read(:pushes) }.by(1)
end
end
end
@@ -197,7 +247,7 @@ describe PostReceive do
# a second to ensure we see changes.
Timecop.freeze(1.second.from_now) do
expect do
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
project.reload
end.to change(project, :last_activity_at)
.and change(project, :last_repository_updated_at)
@@ -208,7 +258,8 @@ describe PostReceive do
context "webhook" do
it "fetches the correct project" do
expect(Project).to receive(:find_by).with(id: project.id.to_s)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+
+ perform
end
it "does not run if the author is not in the project" do
@@ -218,7 +269,7 @@ describe PostReceive do
expect(project).not_to receive(:execute_hooks)
- expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be_falsey
+ expect(perform).to be_falsey
end
it "asks the project to trigger all hooks" do
@@ -227,7 +278,7 @@ describe PostReceive do
expect(project).to receive(:execute_hooks).twice
expect(project).to receive(:execute_services).twice
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
end
it "enqueues a UpdateMergeRequestsWorker job" do
@@ -235,7 +286,7 @@ describe PostReceive do
expect(UpdateMergeRequestsWorker).to receive(:perform_async).with(project.id, project.owner.id, any_args)
- described_class.new.perform(gl_repository, key_id, base64_changes)
+ perform
end
end
end
diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb
index 4de51ecb3e9..66d517332ba 100644
--- a/spec/workers/repository_update_remote_mirror_worker_spec.rb
+++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb
@@ -2,99 +2,70 @@
require 'rails_helper'
-describe RepositoryUpdateRemoteMirrorWorker do
+describe RepositoryUpdateRemoteMirrorWorker, :clean_gitlab_redis_shared_state do
subject { described_class.new }
- let(:remote_mirror) { create(:project, :repository, :remote_mirror).remote_mirrors.first }
+ let(:remote_mirror) { create(:remote_mirror) }
let(:scheduled_time) { Time.now - 5.minutes }
around do |example|
Timecop.freeze(Time.now) { example.run }
end
- describe '#perform' do
- context 'with status none' do
- before do
- remote_mirror.update(update_status: 'none')
- end
-
- it 'sets status as finished when update remote mirror service executes successfully' do
- expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success)
-
- expect { subject.perform(remote_mirror.id, Time.now) }.to change { remote_mirror.reload.update_status }.to('finished')
- end
-
- it 'resets the notification flag upon success' do
- expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success)
- remote_mirror.update_column(:error_notification_sent, true)
-
- expect { subject.perform(remote_mirror.id, Time.now) }.to change { remote_mirror.reload.error_notification_sent }.to(false)
- end
-
- it 'sets status as failed when update remote mirror service executes with errors' do
- error_message = 'fail!'
-
- expect_next_instance_of(Projects::UpdateRemoteMirrorService) do |service|
- expect(service).to receive(:execute).with(remote_mirror).and_return(status: :error, message: error_message)
- end
+ def expect_mirror_service_to_return(mirror, result, tries = 0)
+ expect_next_instance_of(Projects::UpdateRemoteMirrorService) do |service|
+ expect(service).to receive(:execute).with(mirror, tries).and_return(result)
+ end
+ end
- # Mock the finder so that it returns an object we can set expectations on
- expect_next_instance_of(RemoteMirrorFinder) do |finder|
- expect(finder).to receive(:execute).and_return(remote_mirror)
- end
- expect(remote_mirror).to receive(:mark_as_failed).with(error_message)
+ describe '#perform' do
+ it 'calls out to the service to perform the update' do
+ expect_mirror_service_to_return(remote_mirror, status: :success)
- expect do
- subject.perform(remote_mirror.id, Time.now)
- end.to raise_error(RepositoryUpdateRemoteMirrorWorker::UpdateError, error_message)
- end
+ subject.perform(remote_mirror.id, scheduled_time)
+ end
- it 'does nothing if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update(last_update_started_at: Time.now)
+ it 'does not do anything if the mirror was already updated' do
+ remote_mirror.update(last_update_started_at: Time.now, update_status: :finished)
- expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(true)
- expect_any_instance_of(Projects::UpdateRemoteMirrorService).not_to receive(:execute).with(remote_mirror)
+ expect(Projects::UpdateRemoteMirrorService).not_to receive(:new)
- expect(subject.perform(remote_mirror.id, scheduled_time)).to be_nil
- end
+ subject.perform(remote_mirror.id, scheduled_time)
end
- context 'with unexpected error' do
- it 'marks mirror as failed' do
- allow_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_raise(RuntimeError)
+ it 'schedules a retry when the mirror is marked for retrying' do
+ remote_mirror = create(:remote_mirror, update_status: :to_retry)
+ expect_mirror_service_to_return(remote_mirror, status: :error, message: 'Retry!')
- expect do
- subject.perform(remote_mirror.id, Time.now)
- end.to raise_error(RepositoryUpdateRemoteMirrorWorker::UpdateError)
- expect(remote_mirror.reload.update_status).to eq('failed')
- end
- end
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(remote_mirror.backoff_delay, remote_mirror.id, scheduled_time, 1)
- context 'with another worker already running' do
- before do
- remote_mirror.update(update_status: 'started')
- end
-
- it 'raises RemoteMirrorUpdateAlreadyInProgressError' do
- expect do
- subject.perform(remote_mirror.id, Time.now)
- end.to raise_error(RepositoryUpdateRemoteMirrorWorker::UpdateAlreadyInProgressError)
- end
+ subject.perform(remote_mirror.id, scheduled_time)
end
- context 'with status failed' do
- before do
- remote_mirror.update(update_status: 'failed')
+ it 'clears the lease if there was an unexpected exception' do
+ expect_next_instance_of(Projects::UpdateRemoteMirrorService) do |service|
+ expect(service).to receive(:execute).with(remote_mirror, 1).and_raise('Unexpected!')
end
+ expect { subject.perform(remote_mirror.id, Time.now, 1) }.to raise_error('Unexpected!')
- it 'sets status as finished if last_update_started_at is higher than the time the job was scheduled in' do
- remote_mirror.update(last_update_started_at: Time.now)
+ lease = Gitlab::ExclusiveLease.new("#{described_class.name}:#{remote_mirror.id}", timeout: 1.second)
- expect_any_instance_of(RemoteMirror).to receive(:updated_since?).with(scheduled_time).and_return(false)
- expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :success)
+ expect(lease.try_obtain).not_to be_nil
+ end
- expect { subject.perform(remote_mirror.id, scheduled_time) }.to change { remote_mirror.reload.update_status }.to('finished')
- end
+ it 'retries 3 times for the worker to finish before rescheduling' do
+ expect(subject).to receive(:in_lock)
+ .with("#{described_class.name}:#{remote_mirror.id}",
+ retries: 3,
+ ttl: remote_mirror.max_runtime,
+ sleep_sec: described_class::LOCK_WAIT_TIME)
+ .and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
+ expect(described_class).to receive(:perform_in)
+ .with(remote_mirror.backoff_delay, remote_mirror.id, scheduled_time, 0)
+
+ subject.perform(remote_mirror.id, scheduled_time)
end
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"