summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/reports.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml4
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile10
-rw-r--r--Gemfile.lock41
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue15
-rw-r--r--app/assets/javascripts/boards/mixins/issue_card_inner.js5
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue56
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue7
-rw-r--r--app/assets/javascripts/clusters/components/uninstall_application_button.vue14
-rw-r--r--app/assets/javascripts/clusters/constants.js15
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js55
-rw-r--r--app/assets/javascripts/due_date_select.js4
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js5
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/monitoring_store.js7
-rw-r--r--app/assets/javascripts/notes/components/discussion_actions.vue58
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue46
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue28
-rw-r--r--app/assets/stylesheets/components/dashboard_skeleton.scss15
-rw-r--r--app/assets/stylesheets/performance_bar.scss8
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/clusters/clusters_controller.rb3
-rw-r--r--app/finders/autocomplete/users_finder.rb10
-rw-r--r--app/models/clusters/applications/knative.rb2
-rw-r--r--app/models/namespace.rb5
-rw-r--r--app/services/clusters/refresh_service.rb6
-rw-r--r--app/views/notify/links/ci/builds/_build.text.erb2
-rw-r--r--app/views/projects/_merge_request_merge_checks_settings.html.haml19
-rw-r--r--app/views/projects/_merge_request_merge_method_settings.html.haml49
-rw-r--r--app/views/projects/_merge_request_merge_options_settings.html.haml14
-rw-r--r--app/views/projects/_merge_request_merge_settings.html.haml24
-rw-r--r--app/views/projects/_merge_request_settings.html.haml4
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/issues/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml7
-rw-r--r--app/views/shared/_label_row.html.haml11
-rw-r--r--app/views/shared/boards/components/sidebar/_due_date.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/workers/cluster_configure_worker.rb6
-rw-r--r--app/workers/cluster_project_configure_worker.rb2
-rw-r--r--changelogs/unreleased/10012-move-ee-diff-for-boards-issue-card-inner.yml5
-rw-r--r--changelogs/unreleased/57815.yml5
-rw-r--r--changelogs/unreleased/58293-extract-discussion-actions.yml5
-rw-r--r--changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml5
-rw-r--r--changelogs/unreleased/add-runner-access-level-registration.yml5
-rw-r--r--changelogs/unreleased/an-use-labkit.yml5
-rw-r--r--changelogs/unreleased/ce-10725-restructure-project-merge-request-settings-page.yml5
-rw-r--r--changelogs/unreleased/gitlab-issue-54894.yml6
-rw-r--r--changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml5
-rw-r--r--changelogs/unreleased/knative-0-5.yml5
-rw-r--r--changelogs/unreleased/markdown-autocomplete-escaping.yml5
-rw-r--r--changelogs/unreleased/rename_auto_deploy_app_links.yml5
-rw-r--r--changelogs/unreleased/sh-add-gitaly-backtrace.yml5
-rw-r--r--changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml5
-rw-r--r--changelogs/unreleased/sh-bump-ruby-required-version-check.yml5
-rw-r--r--changelogs/unreleased/tc-raw-log-in-plaintext-mail.yml5
-rw-r--r--changelogs/unreleased/weimeng-user-autocomplete-fix.yml5
-rw-r--r--changelogs/unreleased/xanf-gitlab-ce-add-template-link.yml5
-rw-r--r--config/initializers/lograge.rb2
-rw-r--r--config/initializers/peek.rb2
-rw-r--r--config/initializers/tracing.rb16
-rw-r--r--config/prometheus/common_metrics.yml2
-rw-r--r--db/migrate/20190408163745_prometheus_knative05_fix.rb20
-rw-r--r--db/schema.rb2
-rw-r--r--doc/api/projects.md7
-rw-r--r--doc/api/runners.md1
-rw-r--r--doc/api/services.md4
-rw-r--r--doc/ci/merge_request_pipelines/index.md2
-rw-r--r--doc/ci/variables/README.md21
-rw-r--r--doc/ci/yaml/README.md12
-rw-r--r--doc/development/documentation/index.md4
-rw-r--r--doc/development/documentation/styleguide.md77
-rw-r--r--doc/development/gitaly.md2
-rw-r--r--doc/development/testing_guide/testing_levels.md29
-rw-r--r--doc/integration/img/ultra_auth_credentials.pngbin0 -> 53737 bytes
-rw-r--r--doc/integration/img/ultra_auth_edit_callback_url.pngbin0 -> 39410 bytes
-rw-r--r--doc/integration/img/ultra_auth_edit_callback_url_highlighted.pngbin0 -> 68383 bytes
-rw-r--r--doc/integration/omniauth.md1
-rw-r--r--doc/integration/ultra_auth.md78
-rw-r--r--doc/topics/autodevops/index.md8
-rw-r--r--doc/user/admin_area/settings/external_authorization.md112
-rw-r--r--doc/user/admin_area/settings/img/classification_label_on_project_page.pngbin0 -> 19568 bytes
-rw-r--r--doc/user/admin_area/settings/img/external_authorization_service_settings.pngbin0 -> 74753 bytes
-rw-r--r--doc/user/discussions/index.md16
-rw-r--r--doc/user/project/clusters/index.md4
-rw-r--r--doc/user/project/clusters/serverless/index.md15
-rw-r--r--lib/api/entities.rb10
-rw-r--r--lib/api/runner.rb4
-rw-r--r--lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb4
-rw-r--r--lib/gitlab/git/rugged_impl/commit.rb20
-rw-r--r--lib/gitlab/git/rugged_impl/repository.rb2
-rw-r--r--lib/gitlab/gitaly_client.rb19
-rw-r--r--lib/gitlab/grape_logging/loggers/correlation_id_logger.rb2
-rw-r--r--lib/gitlab/json_logger.rb2
-rw-r--r--lib/gitlab/middleware/correlation_id.rb2
-rw-r--r--lib/gitlab/profiler.rb4
-rw-r--r--lib/gitlab/sentry.rb2
-rw-r--r--lib/gitlab/sidekiq_middleware/correlation_injector.rb4
-rw-r--r--lib/gitlab/sidekiq_middleware/correlation_logger.rb4
-rw-r--r--lib/gitlab/tracing/common.rb69
-rw-r--r--lib/gitlab/tracing/factory.rb61
-rw-r--r--lib/gitlab/tracing/grpc_interceptor.rb54
-rw-r--r--lib/gitlab/tracing/jaeger_factory.rb97
-rw-r--r--lib/gitlab/tracing/rack_middleware.rb46
-rw-r--r--lib/gitlab/tracing/rails/action_view_subscriber.rb75
-rw-r--r--lib/gitlab/tracing/rails/active_record_subscriber.rb49
-rw-r--r--lib/gitlab/tracing/rails/rails_common.rb24
-rw-r--r--lib/gitlab/tracing/sidekiq/client_middleware.rb26
-rw-r--r--lib/gitlab/tracing/sidekiq/server_middleware.rb26
-rw-r--r--lib/gitlab/tracing/sidekiq/sidekiq_common.rb22
-rw-r--r--lib/peek/views/tracing.rb6
-rw-r--r--lib/system_check/app/ruby_version_check.rb2
-rw-r--r--locale/gitlab.pot104
-rw-r--r--qa/qa/page/component/note.rb2
-rw-r--r--spec/controllers/application_controller_spec.rb2
-rw-r--r--spec/features/issues_spec.rb4
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb2
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb25
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb4
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb16
-rw-r--r--spec/frontend/clusters/components/application_row_spec.js28
-rw-r--r--spec/frontend/clusters/stores/clusters_store_spec.js23
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js21
-rw-r--r--spec/frontend/notes/components/discussion_actions_spec.js104
-rw-r--r--spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js3
-rw-r--r--spec/javascripts/boards/board_list_common_spec.js58
-rw-r--r--spec/javascripts/boards/board_list_spec.js51
-rw-r--r--spec/javascripts/boards/issue_card_spec.js6
-rw-r--r--spec/javascripts/monitoring/monitoring_store_spec.js24
-rw-r--r--spec/javascripts/performance_bar/components/detailed_metric_spec.js22
-rw-r--r--spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb10
-rw-r--r--spec/lib/gitlab/correlation_id_spec.rb77
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb37
-rw-r--r--spec/lib/gitlab/json_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/sentry_spec.rb4
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb2
-rw-r--r--spec/lib/gitlab/tracing/factory_spec.rb43
-rw-r--r--spec/lib/gitlab/tracing/grpc_interceptor_spec.rb47
-rw-r--r--spec/lib/gitlab/tracing/jaeger_factory_spec.rb71
-rw-r--r--spec/lib/gitlab/tracing/rack_middleware_spec.rb62
-rw-r--r--spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb147
-rw-r--r--spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb73
-rw-r--r--spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb43
-rw-r--r--spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb43
-rw-r--r--spec/models/clusters/applications/knative_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb22
-rw-r--r--spec/rack_servers/configs/puma.rb32
-rw-r--r--spec/rack_servers/puma_spec.rb20
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb36
-rw-r--r--spec/requests/api/runner_spec.rb26
-rw-r--r--spec/services/clusters/refresh_service_spec.rb26
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/helpers/wait_for_requests.rb14
-rw-r--r--spec/support/helpers/wait_helpers.rb20
-rw-r--r--spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb2
-rw-r--r--spec/views/notify/pipeline_failed_email.text.erb_spec.rb39
-rw-r--r--spec/views/shared/_label_row.html.haml.rb32
-rw-r--r--spec/workers/cluster_configure_worker_spec.rb67
-rw-r--r--spec/workers/cluster_project_configure_worker_spec.rb15
165 files changed, 1553 insertions, 1712 deletions
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 5acfdb4e832..fd179f77e26 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -104,6 +104,7 @@ gitlab:ui:visual:
artifacts:
paths:
- tests/__image_snapshots__/
+ when: always
karma:
extends: .dedicated-no-docs-pull-cache-job
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index c48672183b9..466c47b37c7 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -1,6 +1,6 @@
.dedicated-runner:
retry:
- max: 1 # This is confusing but this means "2 runs at max".
+ max: 2 # This is confusing but this means "3 runs at max".
when:
- unknown_failure
- api_failure
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index 9655498073e..df1fec61c25 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -2,7 +2,7 @@ include:
- local: /lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
code_quality:
- extends: .dedicated-runner
+ extends: .dedicated-no-docs-no-db-pull-cache-job
# gitlab-org runners set `privileged: false` but we need to have it set to true
# since we're using Docker in Docker
tags: []
@@ -13,7 +13,7 @@ code_quality:
SETUP_DB: "false"
sast:
- extends: .dedicated-runner
+ extends: .dedicated-no-docs-no-db-pull-cache-job
image: docker:stable
variables:
SAST_CONFIDENCE_LEVEL: 2
@@ -37,7 +37,7 @@ sast:
sast: gl-sast-report.json
dependency_scanning:
- extends: .dedicated-runner
+ extends: .dedicated-no-docs-no-db-pull-cache-job
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index a7f6caedd37..dee72930a28 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -83,7 +83,6 @@ schedule:review-build-cng:
<<: *review-schedules-only
<<: *review-build-cng-base
-
.review-deploy-base: &review-deploy-base
<<: *review-base
allow_failure: true
@@ -145,6 +144,7 @@ schedule:review-deploy:
review-qa-smoke:
<<: *review-qa-base
+ retry: 2
script:
- wait_for_job_to_be_done "review-deploy"
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
@@ -175,6 +175,7 @@ review-performance:
<<: *review-performance-base
review-stop:
+ <<: *review-base
extends: .single-script-job-dedicated-runner
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
allow_failure: true
@@ -229,4 +230,3 @@ danger-review:
- node --version
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- danger --fail-on-errors=true
-
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7aa332e4163..2b17ffd5042 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.33.0
+1.34.0
diff --git a/Gemfile b/Gemfile
index c55e6478cb0..eaaf8125009 100644
--- a/Gemfile
+++ b/Gemfile
@@ -42,6 +42,7 @@ gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.3'
gem 'rack-oauth2', '~> 1.9.3'
+gem "omniauth-ultraauth", '~> 0.0.1'
gem 'jwt', '~> 2.1.0'
# Spam and anti-bot protection
@@ -273,6 +274,9 @@ gem 'sentry-raven', '~> 2.7'
gem 'premailer-rails', '~> 1.9.7'
+# LabKit: Tracing and Correlation
+gem 'gitlab-labkit', '~> 0.1.2'
+
# I18n
gem 'ruby_parser', '~> 3.8', require: false
gem 'rails-i18n', '~> 5.1'
@@ -300,12 +304,6 @@ group :metrics do
gem 'raindrops', '~> 0.18'
end
-group :tracing do
- # OpenTracing
- gem 'opentracing', '~> 0.4.3'
- gem 'jaeger-client', '~> 0.10.0'
-end
-
group :development do
gem 'foreman', '~> 0.84.0'
gem 'brakeman', '~> 4.2', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 109958e2591..ba4418cd8b3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -286,6 +286,12 @@ GEM
github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
activerecord (>= 3.2.0, < 6.0)
+ gitlab-labkit (0.1.2)
+ actionpack (~> 5)
+ activesupport (~> 5)
+ grpc (~> 1.15)
+ jaeger-client (~> 0.10)
+ opentracing (~> 0.4)
gitlab-markup (1.7.0)
gitlab-sidekiq-fetcher (0.4.0)
sidekiq (~> 5)
@@ -551,11 +557,27 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
+ omniauth-ultraauth (0.0.1)
+ omniauth_openid_connect (~> 0.2.4)
omniauth_crowd (2.2.3)
activesupport
nokogiri (>= 1.4.4)
omniauth (~> 1.0)
- opentracing (0.4.3)
+ omniauth_openid_connect (0.2.4)
+ addressable (~> 2.5)
+ omniauth (~> 1.3)
+ openid_connect (~> 1.1)
+ openid_connect (1.1.6)
+ activemodel
+ attr_required (>= 1.0.0)
+ json-jwt (>= 1.5.0)
+ rack-oauth2 (>= 1.6.1)
+ swd (>= 1.0.0)
+ tzinfo
+ validate_email
+ validate_url
+ webfinger (>= 1.0.1)
+ opentracing (0.5.0)
optimist (3.0.0)
org-ruby (0.9.12)
rubypants (~> 0.2)
@@ -865,6 +887,10 @@ GEM
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
+ swd (1.1.2)
+ activesupport (>= 3)
+ attr_required (>= 0.0.5)
+ httpclient (>= 2.4)
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
@@ -914,6 +940,12 @@ GEM
equalizer (~> 0.0.9)
parser (>= 2.3.1.2, < 2.6)
procto (~> 0.0.2)
+ validate_email (0.1.6)
+ activemodel (>= 3.0)
+ mail (>= 2.2.5)
+ validate_url (1.0.8)
+ activemodel (>= 3.0.0)
+ public_suffix
validates_hostname (1.0.6)
activerecord (>= 3.0)
activesupport (>= 3.0)
@@ -926,6 +958,9 @@ GEM
vmstat (2.3.0)
warden (1.2.7)
rack (>= 1.0)
+ webfinger (1.1.0)
+ activesupport
+ httpclient (>= 2.4)
webmock (3.5.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
@@ -1021,6 +1056,7 @@ DEPENDENCIES
gitaly-proto (~> 1.19.0)
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
+ gitlab-labkit (~> 0.1.2)
gitlab-markup (~> 1.7.0)
gitlab-sidekiq-fetcher (~> 0.4.0)
gitlab-styles (~> 2.5)
@@ -1047,7 +1083,6 @@ DEPENDENCIES
httparty (~> 0.16.4)
icalendar
influxdb (~> 0.2)
- jaeger-client (~> 0.10.0)
jira-ruby (~> 1.4)
js_regex (~> 3.1)
json-schema (~> 2.8.0)
@@ -1086,8 +1121,8 @@ DEPENDENCIES
omniauth-saml (~> 1.10)
omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
+ omniauth-ultraauth (~> 0.0.1)
omniauth_crowd (~> 2.2.0)
- opentracing (~> 0.4.3)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 7c03f06e650..6aa689b4056 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -4,6 +4,7 @@ import { GlTooltipDirective } from '@gitlab/ui';
import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub';
import IssueDueDate from './issue_due_date.vue';
@@ -19,11 +20,13 @@ export default {
TooltipOnTruncate,
IssueDueDate,
IssueTimeEstimate,
+ IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
IssueCardInnerScopedLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [issueCardInner],
props: {
issue: {
type: Object,
@@ -135,14 +138,6 @@ export default {
this.applyFilter(filter);
},
- filterByWeight(weight) {
- if (!this.updateFilters) return;
-
- const issueWeight = encodeURIComponent(weight);
- const filter = `weight=${issueWeight}`;
-
- this.applyFilter(filter);
- },
applyFilter(filter) {
const filterPath = boardsStore.filter.path.split('&');
const filterIndex = filterPath.indexOf(filter);
@@ -232,6 +227,10 @@ export default {
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" /><issue-time-estimate
v-if="issue.timeEstimate"
:estimate="issue.timeEstimate"
+ /><issue-card-weight
+ v-if="issue.weight"
+ :weight="issue.weight"
+ @click="filterByWeight(issue.weight)"
/>
</span>
</div>
diff --git a/app/assets/javascripts/boards/mixins/issue_card_inner.js b/app/assets/javascripts/boards/mixins/issue_card_inner.js
new file mode 100644
index 00000000000..8000237da6d
--- /dev/null
+++ b/app/assets/javascripts/boards/mixins/issue_card_inner.js
@@ -0,0 +1,5 @@
+export default {
+ methods: {
+ filterByWeight() {},
+ },
+};
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 19e5ac1567d..937e4c3bfc3 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -6,6 +6,8 @@ import { s__, sprintf } from '../../locale';
import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
+import UninstallApplicationButton from './uninstall_application_button.vue';
+
import {
APPLICATION_STATUS,
REQUEST_SUBMITTED,
@@ -19,6 +21,7 @@ export default {
identicon,
TimeagoTooltip,
GlLink,
+ UninstallApplicationButton,
},
props: {
id: {
@@ -47,6 +50,11 @@ export default {
required: false,
default: false,
},
+ uninstallable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
status: {
type: String,
required: false,
@@ -63,6 +71,11 @@ export default {
type: String,
required: false,
},
+ installed: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
version: {
type: String,
required: false,
@@ -92,15 +105,7 @@ export default {
return (
this.status === APPLICATION_STATUS.SCHEDULED ||
this.status === APPLICATION_STATUS.INSTALLING ||
- (this.requestStatus === REQUEST_SUBMITTED && !this.statusReason && !this.isInstalled)
- );
- },
- isInstalled() {
- return (
- this.status === APPLICATION_STATUS.INSTALLED ||
- this.status === APPLICATION_STATUS.UPDATED ||
- this.status === APPLICATION_STATUS.UPDATING ||
- this.status === APPLICATION_STATUS.UPDATE_ERRORED
+ (this.requestStatus === REQUEST_SUBMITTED && !this.statusReason && !this.installed)
);
},
canInstall() {
@@ -125,6 +130,12 @@ export default {
rowJsClass() {
return `js-cluster-application-row-${this.id}`;
},
+ displayUninstallButton() {
+ return this.installed && this.uninstallable;
+ },
+ displayInstallButton() {
+ return !this.installed || !this.uninstallable;
+ },
installButtonLoading() {
return !this.status || this.status === APPLICATION_STATUS.SCHEDULED || this.isInstalling;
},
@@ -145,7 +156,7 @@ export default {
label = s__('ClusterIntegration|Install');
} else if (this.isInstalling) {
label = s__('ClusterIntegration|Installing');
- } else if (this.isInstalled) {
+ } else if (this.installed) {
label = s__('ClusterIntegration|Installed');
}
@@ -257,7 +268,7 @@ export default {
<div
:class="[
rowJsClass,
- isInstalled && 'cluster-application-installed',
+ installed && 'cluster-application-installed',
disabled && 'cluster-application-disabled',
]"
class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
@@ -280,10 +291,9 @@ export default {
target="blank"
rel="noopener noreferrer"
class="js-cluster-application-title"
+ >{{ title }}</a
>
- {{ title }}
- </a>
- <span v-else class="js-cluster-application-title"> {{ title }} </span>
+ <span v-else class="js-cluster-application-title">{{ title }}</span>
</strong>
<slot name="description"></slot>
<div
@@ -308,17 +318,15 @@ export default {
class="form-text text-muted label p-0 js-cluster-application-upgrade-details"
>
{{ versionLabel }}
-
- <span v-if="upgradeSuccessful"> to</span>
+ <span v-if="upgradeSuccessful">to</span>
<gl-link
v-if="upgradeSuccessful"
:href="chartRepo"
target="_blank"
class="js-cluster-application-upgrade-version"
+ >chart v{{ version }}</gl-link
>
- chart v{{ version }}
- </gl-link>
</div>
<div
@@ -333,7 +341,6 @@ export default {
class="bs-callout bs-callout-success cluster-application-banner mt-2 mb-0 p-0 pl-3"
>
{{ upgradeSuccessDescription }}
-
<button class="close cluster-application-banner-close" @click="dismissUpgradeSuccess">
&times;
</button>
@@ -354,18 +361,23 @@ export default {
role="gridcell"
>
<div v-if="showManageButton" class="btn-group table-action-buttons">
- <a :href="manageLink" :class="{ disabled: disabled }" class="btn">
- {{ manageButtonLabel }}
- </a>
+ <a :href="manageLink" :class="{ disabled: disabled }" class="btn">{{
+ manageButtonLabel
+ }}</a>
</div>
<div class="btn-group table-action-buttons">
<loading-button
+ v-if="displayInstallButton"
:loading="installButtonLoading"
:disabled="disabled || installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
@click="installClicked"
/>
+ <uninstall-application-button
+ v-if="displayUninstallButton"
+ class="js-cluster-application-uninstall-button"
+ />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index d54f9ce552c..ae4fe11c6ae 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -238,6 +238,7 @@ export default {
:status-reason="applications.helm.statusReason"
:request-status="applications.helm.requestStatus"
:request-reason="applications.helm.requestReason"
+ :installed="applications.helm.installed"
class="rounded-top"
title-link="https://docs.helm.sh/"
>
@@ -265,6 +266,7 @@ export default {
:status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason"
+ :installed="applications.ingress.installed"
:disabled="!helmInstalled"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
>
@@ -341,6 +343,7 @@ export default {
:status-reason="applications.cert_manager.statusReason"
:request-status="applications.cert_manager.requestStatus"
:request-reason="applications.cert_manager.requestReason"
+ :installed="applications.cert_manager.installed"
:install-application-request-params="{ email: applications.cert_manager.email }"
:disabled="!helmInstalled"
title-link="https://cert-manager.readthedocs.io/en/latest/#"
@@ -387,6 +390,7 @@ export default {
:status-reason="applications.prometheus.statusReason"
:request-status="applications.prometheus.requestStatus"
:request-reason="applications.prometheus.requestReason"
+ :installed="applications.prometheus.installed"
:disabled="!helmInstalled"
title-link="https://prometheus.io/docs/introduction/overview/"
>
@@ -403,6 +407,7 @@ export default {
:version="applications.runner.version"
:chart-repo="applications.runner.chartRepo"
:upgrade-available="applications.runner.upgradeAvailable"
+ :installed="applications.runner.installed"
:disabled="!helmInstalled"
title-link="https://docs.gitlab.com/runner/"
>
@@ -424,6 +429,7 @@ export default {
:status-reason="applications.jupyter.statusReason"
:request-status="applications.jupyter.requestStatus"
:request-reason="applications.jupyter.requestReason"
+ :installed="applications.jupyter.installed"
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
:disabled="!helmInstalled"
title-link="https://jupyterhub.readthedocs.io/en/stable/"
@@ -483,6 +489,7 @@ export default {
:status-reason="applications.knative.statusReason"
:request-status="applications.knative.requestStatus"
:request-reason="applications.knative.requestReason"
+ :installed="applications.knative.installed"
:install-application-request-params="{ hostname: applications.knative.hostname }"
:disabled="!helmInstalled"
title-link="https://github.com/knative/docs"
diff --git a/app/assets/javascripts/clusters/components/uninstall_application_button.vue b/app/assets/javascripts/clusters/components/uninstall_application_button.vue
new file mode 100644
index 00000000000..30918d1d115
--- /dev/null
+++ b/app/assets/javascripts/clusters/components/uninstall_application_button.vue
@@ -0,0 +1,14 @@
+<script>
+// TODO: Implement loading button component
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+
+export default {
+ components: {
+ LoadingButton,
+ },
+};
+</script>
+
+<template>
+ <loading-button @click="$emit('click')" />
+</template>
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index 67f481f2afb..17849497c87 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -15,9 +15,24 @@ export const APPLICATION_STATUS = {
UPDATING: 'updating',
UPDATED: 'updated',
UPDATE_ERRORED: 'update_errored',
+ UNINSTALLING: 'uninstalling',
+ UNINSTALL_ERRORED: 'uninstall_errored',
ERROR: 'errored',
};
+/*
+ * The application cannot be in any of the following states without
+ * not being installed.
+ */
+export const APPLICATION_INSTALLED_STATUSES = [
+ APPLICATION_STATUS.INSTALLED,
+ APPLICATION_STATUS.UPDATING,
+ APPLICATION_STATUS.UPDATED,
+ APPLICATION_STATUS.UPDATE_ERRORED,
+ APPLICATION_STATUS.UNINSTALLING,
+ APPLICATION_STATUS.UNINSTALL_ERRORED,
+];
+
// These are only used client-side
export const REQUEST_SUBMITTED = 'request-submitted';
export const REQUEST_FAILURE = 'request-failure';
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 92993337f02..38512ac28c2 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -1,6 +1,23 @@
import { s__ } from '../../locale';
import { parseBoolean } from '../../lib/utils/common_utils';
-import { INGRESS, JUPYTER, KNATIVE, CERT_MANAGER, RUNNER } from '../constants';
+import {
+ INGRESS,
+ JUPYTER,
+ KNATIVE,
+ CERT_MANAGER,
+ RUNNER,
+ APPLICATION_INSTALLED_STATUSES,
+} from '../constants';
+
+const isApplicationInstalled = appStatus => APPLICATION_INSTALLED_STATUSES.includes(appStatus);
+
+const applicationInitialState = {
+ status: null,
+ statusReason: null,
+ requestReason: null,
+ requestStatus: null,
+ installed: false,
+};
export default class ClusterStore {
constructor() {
@@ -12,60 +29,39 @@ export default class ClusterStore {
statusReason: null,
applications: {
helm: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|Helm Tiller'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
},
ingress: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|Ingress'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
externalIp: null,
externalHostname: null,
},
cert_manager: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|Cert-Manager'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
email: null,
},
runner: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|GitLab Runner'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
version: null,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
upgradeAvailable: null,
},
prometheus: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|Prometheus'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
},
jupyter: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|JupyterHub'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
hostname: null,
},
knative: {
+ ...applicationInitialState,
title: s__('ClusterIntegration|Knative'),
- status: null,
- statusReason: null,
- requestStatus: null,
- requestReason: null,
hostname: null,
isEditingHostName: false,
externalIp: null,
@@ -118,6 +114,7 @@ export default class ClusterStore {
...(this.state.applications[appId] || {}),
status,
statusReason,
+ installed: isApplicationInstalled(status),
};
if (appId === INGRESS) {
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index cb1b1173190..3c650397a19 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -104,7 +104,7 @@ class DueDateSelect {
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy');
} else {
- this.displayedDate = 'No due date';
+ this.displayedDate = __('None');
}
}
@@ -132,7 +132,7 @@ class DueDateSelect {
submitSelectedDate(isDropdown) {
const selectedDateValue = this.datePayload[this.abilityName].due_date;
- const hasDueDate = this.displayedDate !== 'No due date';
+ const hasDueDate = this.displayedDate !== __('None');
const displayedDateStyle = hasDueDate ? 'bold' : 'no-value';
this.$loading.removeClass('hidden').fadeIn();
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index c81e754df4c..f1e26cdfa21 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -461,7 +461,10 @@ class GfmAutoComplete {
// We can ignore this for quick actions because they are processed
// before Markdown.
if (!this.setting.skipMarkdownCharacterTest) {
- withoutAt = withoutAt.replace(/([~\-_*`])/g, '\\$&');
+ withoutAt = withoutAt
+ .replace(/(~~|`|\*)/g, '\\$1')
+ .replace(/(\b)(_+)/g, '$1\\$2') // only escape underscores at the start
+ .replace(/(_+)(\b)/g, '\\$1$2'); // or end of words
}
return `${at}${withoutAt}`;
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 2b4ddd7afbc..ed794779ff2 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Dashboard from './components/dashboard.vue';
-export default () => {
+export default (props = {}) => {
const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) {
@@ -15,6 +15,7 @@ export default () => {
...el.dataset,
hasMetrics: parseBoolean(el.dataset.hasMetrics),
showTimeWindowDropdown: gon.features.metricsTimeWindow,
+ ...props,
},
});
},
diff --git a/app/assets/javascripts/monitoring/stores/monitoring_store.js b/app/assets/javascripts/monitoring/stores/monitoring_store.js
index 9761fe168be..013fb0d4540 100644
--- a/app/assets/javascripts/monitoring/stores/monitoring_store.js
+++ b/app/assets/javascripts/monitoring/stores/monitoring_store.js
@@ -45,14 +45,13 @@ function removeTimeSeriesNoData(queries) {
// ]
function groupQueriesByChartInfo(metrics) {
const metricsByChart = metrics.reduce((accumulator, metric) => {
- const { id, queries, ...chart } = metric;
+ const { queries, ...chart } = metric;
+ const metricId = chart.id ? chart.id.toString() : null;
const chartKey = `${chart.title}|${chart.y_label}`;
accumulator[chartKey] = accumulator[chartKey] || { ...chart, queries: [] };
- queries.forEach(queryAttrs =>
- accumulator[chartKey].queries.push({ metricId: id.toString(), ...queryAttrs }),
- );
+ queries.forEach(queryAttrs => accumulator[chartKey].queries.push({ metricId, ...queryAttrs }));
return accumulator;
}, {});
diff --git a/app/assets/javascripts/notes/components/discussion_actions.vue b/app/assets/javascripts/notes/components/discussion_actions.vue
new file mode 100644
index 00000000000..22cca756ef6
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_actions.vue
@@ -0,0 +1,58 @@
+<script>
+import ReplyPlaceholder from './discussion_reply_placeholder.vue';
+import ResolveDiscussionButton from './discussion_resolve_button.vue';
+import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
+import JumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
+
+export default {
+ name: 'DiscussionActions',
+ components: {
+ ReplyPlaceholder,
+ ResolveDiscussionButton,
+ ResolveWithIssueButton,
+ JumpToNextDiscussionButton,
+ },
+ props: {
+ discussion: {
+ type: Object,
+ required: true,
+ },
+ isResolving: {
+ type: Boolean,
+ required: true,
+ },
+ resolveButtonTitle: {
+ type: String,
+ required: true,
+ },
+ resolveWithIssuePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ shouldShowJumpToNextDiscussion: {
+ type: Boolean,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="discussion-with-resolve-btn">
+ <reply-placeholder class="qa-discussion-reply" @onClick="$emit('showReplyForm')" />
+ <resolve-discussion-button
+ v-if="discussion.resolvable"
+ :is-resolving="isResolving"
+ :button-title="resolveButtonTitle"
+ @onClick="$emit('resolve')"
+ />
+ <div v-if="discussion.resolvable" class="btn-group discussion-actions ml-sm-2" role="group">
+ <resolve-with-issue-button v-if="resolveWithIssuePath" :url="resolveWithIssuePath" />
+ <jump-to-next-discussion-button
+ v-if="shouldShowJumpToNextDiscussion"
+ @onClick="$emit('jumpToNextDiscussion')"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index a3d664a738f..89563711bcd 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -14,7 +14,6 @@ import { SYSTEM_NOTE } from '../constants';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteableNote from './noteable_note.vue';
import noteHeader from './note_header.vue';
-import resolveDiscussionButton from './discussion_resolve_button.vue';
import toggleRepliesWidget from './toggle_replies_widget.vue';
import noteSignedOutWidget from './note_signed_out_widget.vue';
import noteEditedText from './note_edited_text.vue';
@@ -25,10 +24,8 @@ import placeholderSystemNote from '../../vue_shared/components/notes/placeholder
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
import discussionNavigation from '../mixins/discussion_navigation';
-import ReplyPlaceholder from './discussion_reply_placeholder.vue';
-import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
-import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
import eventHub from '../event_hub';
+import DiscussionActions from './discussion_actions.vue';
export default {
name: 'NoteableDiscussion',
@@ -40,16 +37,13 @@ export default {
noteSignedOutWidget,
noteEditedText,
noteForm,
- resolveDiscussionButton,
- jumpToNextDiscussionButton,
toggleRepliesWidget,
- ReplyPlaceholder,
placeholderNote,
placeholderSystemNote,
- ResolveWithIssueButton,
systemNote,
DraftNote: () => import('ee_component/batch_comments/components/draft_note.vue'),
TimelineEntryItem,
+ DiscussionActions,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -465,31 +459,17 @@ Please check your network connection and try again.`;
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
- <template v-if="!isReplying && canReply">
- <div class="discussion-with-resolve-btn">
- <reply-placeholder class="qa-discussion-reply" @onClick="showReplyForm" />
- <resolve-discussion-button
- v-if="discussion.resolvable"
- :is-resolving="isResolving"
- :button-title="resolveButtonTitle"
- @onClick="resolveHandler"
- />
- <div
- v-if="discussion.resolvable"
- class="btn-group discussion-actions ml-sm-2"
- role="group"
- >
- <resolve-with-issue-button
- v-if="resolveWithIssuePath"
- :url="resolveWithIssuePath"
- />
- <jump-to-next-discussion-button
- v-if="shouldShowJumpToNextDiscussion"
- @onClick="jumpToNextDiscussion"
- />
- </div>
- </div>
- </template>
+ <discussion-actions
+ v-if="!isReplying && canReply"
+ :discussion="discussion"
+ :is-resolving="isResolving"
+ :resolve-button-title="resolveButtonTitle"
+ :resolve-with-issue-path="resolveWithIssuePath"
+ :should-show-jump-to-next-discussion="shouldShowJumpToNextDiscussion"
+ @showReplyForm="showReplyForm"
+ @resolve="resolveHandler"
+ @jumpToNextDiscussion="jumpToNextDiscussion"
+ />
<note-form
v-if="isReplying"
ref="noteForm"
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index 19a81713964..8f3ba9779fb 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -1,9 +1,11 @@
<script>
import GlModal from '~/vue_shared/components/gl_modal.vue';
+import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
GlModal,
+ Icon,
},
props: {
currentRequest: {
@@ -61,9 +63,31 @@ export default {
<template v-if="detailsList.length">
<tr v-for="(item, index) in detailsList" :key="index">
<td>
- <strong>{{ item.duration }}ms</strong>
+ <span>{{ item.duration }}ms</span>
+ </td>
+ <td>
+ <div class="js-toggle-container">
+ <div
+ v-for="(key, keyIndex) in keys"
+ :key="key"
+ class="break-word"
+ :class="{ 'mb-3 bold': keyIndex == 0 }"
+ >
+ {{ item[key] }}
+ <button
+ v-if="keyIndex == 0 && item.backtrace"
+ class="text-expander js-toggle-button"
+ type="button"
+ :aria-label="__('Toggle backtrace')"
+ >
+ <icon :size="12" name="ellipsis_h" />
+ </button>
+ </div>
+ <pre v-if="item.backtrace" class="backtrace-row js-toggle-content mt-2">{{
+ item.backtrace
+ }}</pre>
+ </div>
</td>
- <td v-for="key in keys" :key="key" class="break-word">{{ item[key] }}</td>
</tr>
</template>
<template v-else>
diff --git a/app/assets/stylesheets/components/dashboard_skeleton.scss b/app/assets/stylesheets/components/dashboard_skeleton.scss
index 42ede599bc6..9775c329922 100644
--- a/app/assets/stylesheets/components/dashboard_skeleton.scss
+++ b/app/assets/stylesheets/components/dashboard_skeleton.scss
@@ -8,10 +8,6 @@
&-warning {
background-color: $orange-100;
}
-
- &-failed {
- background-color: $red-100;
- }
}
&-body {
@@ -36,10 +32,6 @@
border-radius: $gl-padding;
height: $gl-padding-32;
- &-failed {
- background-color: $red-100;
- }
-
&-arrow {
color: $gray-300;
}
@@ -56,6 +48,13 @@
}
}
+ &-header,
+ &-footer {
+ &-failed {
+ background-color: $red-100;
+ }
+ }
+
&-skeleton-info {
border-radius: $gl-padding;
height: $gl-padding;
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 9c01a2f8bda..5a8940ffd6d 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -79,8 +79,12 @@
table {
color: $black;
- strong {
- color: $black;
+ td {
+ vertical-align: top;
+ }
+
+ .backtrace-row {
+ display: none;
}
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d5f1e35a79b..ceaa84acaba 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -128,7 +128,7 @@ class ApplicationController < ActionController::Base
payload[:ua] = request.env["HTTP_USER_AGENT"]
payload[:remote_ip] = request.remote_ip
- payload[Gitlab::CorrelationId::LOG_KEY] = Gitlab::CorrelationId.current_id
+ payload[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id
logged_user = auth_user
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index e82756e4643..edaf07063ec 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -12,6 +12,9 @@ class Clusters::ClustersController < Clusters::BaseController
before_action :authorize_update_cluster!, only: [:update]
before_action :authorize_admin_cluster!, only: [:destroy]
before_action :update_applications_status, only: [:cluster_status]
+ before_action only: [:show] do
+ push_frontend_feature_flag(:metrics_time_window)
+ end
helper_method :token_in_session
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index 45955783be9..ce7d0b8699c 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -2,6 +2,8 @@
module Autocomplete
class UsersFinder
+ include Gitlab::Utils::StrongMemoize
+
# The number of users to display in the results is hardcoded to 20, and
# pagination is not supported. This ensures that performance remains
# consistent and removes the need for implementing keyset pagination to
@@ -31,7 +33,7 @@ module Autocomplete
# Include current user if available to filter by "Me"
items.unshift(current_user) if prepend_current_user?
- if prepend_author? && (author = User.find_by_id(author_id))
+ if prepend_author? && author&.active?
items.unshift(author)
end
end
@@ -41,6 +43,12 @@ module Autocomplete
private
+ def author
+ strong_memoize(:author) do
+ User.find_by_id(author_id)
+ end
+ end
+
# Returns the users based on the input parameters, as an Array.
#
# This method is separate so it is easier to extend in EE.
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index f7e54833296..38cbc9ce8eb 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Knative < ApplicationRecord
- VERSION = '0.3.0'.freeze
+ VERSION = '0.5.0'.freeze
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze
METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'.freeze
FETCH_IP_ADDRESS_DELAY = 30.seconds
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index dea34e812ca..8638c5a9c53 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -51,6 +51,7 @@ class Namespace < ApplicationRecord
validate :nesting_level_allowed
delegate :name, to: :owner, allow_nil: true, prefix: true
+ delegate :avatar_url, to: :owner, allow_nil: true
after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
@@ -149,6 +150,10 @@ class Namespace < ApplicationRecord
type == 'Group' ? 'group' : 'user'
end
+ def user?
+ kind == 'user'
+ end
+
def find_fork_of(project)
return unless project.fork_network
diff --git a/app/services/clusters/refresh_service.rb b/app/services/clusters/refresh_service.rb
index 7c82b98a33f..76ad8dd0fb0 100644
--- a/app/services/clusters/refresh_service.rb
+++ b/app/services/clusters/refresh_service.rb
@@ -21,7 +21,11 @@ module Clusters
private_class_method :projects_with_missing_kubernetes_namespaces_for_cluster
def self.clusters_with_missing_kubernetes_namespaces_for_project(project)
- project.all_clusters.missing_kubernetes_namespace(project.kubernetes_namespaces)
+ if Feature.enabled?(:ci_preparing_state, default_enabled: true)
+ project.clusters.missing_kubernetes_namespace(project.kubernetes_namespaces)
+ else
+ project.all_clusters.missing_kubernetes_namespace(project.kubernetes_namespaces)
+ end
end
private_class_method :clusters_with_missing_kubernetes_namespaces_for_project
diff --git a/app/views/notify/links/ci/builds/_build.text.erb b/app/views/notify/links/ci/builds/_build.text.erb
index 773ae8174e9..afb02f97e5a 100644
--- a/app/views/notify/links/ci/builds/_build.text.erb
+++ b/app/views/notify/links/ci/builds/_build.text.erb
@@ -1 +1 @@
-Job #<%= build.id %> ( <%= pipeline_job_url(pipeline, build) %> )
+Job #<%= build.id %> ( <%= raw_project_job_url(pipeline.project, build) %> )
diff --git a/app/views/projects/_merge_request_merge_checks_settings.html.haml b/app/views/projects/_merge_request_merge_checks_settings.html.haml
new file mode 100644
index 00000000000..1ab467a3710
--- /dev/null
+++ b/app/views/projects/_merge_request_merge_checks_settings.html.haml
@@ -0,0 +1,19 @@
+- form = local_assigns.fetch(:form)
+
+.form-group
+ %b= s_('ProjectSettings|Merge checks')
+ %p.text-secondary= s_('ProjectSettings|These checks must pass before merge requests can be merged')
+ .form-check.mb-2.builds-feature{ class: ("hidden" if @project && @project.project_feature.send(:builds_access_level) == 0) }
+ = form.check_box :only_allow_merge_if_pipeline_succeeds, class: 'form-check-input'
+ = form.label :only_allow_merge_if_pipeline_succeeds, class: 'form-check-label' do
+ = s_('ProjectSettings|Pipelines must succeed')
+ .descr.text-secondary
+ = s_('ProjectSettings|Pipelines need to be configured to enable this feature.')
+ = link_to icon('question-circle'),
+ help_page_path('ci/merge_request_pipelines/index.md',
+ anchor: 'pipelines-for-merge-requests'),
+ target: '_blank'
+ .form-check.mb-2
+ = form.check_box :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-input'
+ = form.label :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-label' do
+ = s_('ProjectSettings|All discussions must be resolved')
diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml
index 9082bfc409d..47c311f42d0 100644
--- a/app/views/projects/_merge_request_merge_method_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_method_settings.html.haml
@@ -1,30 +1,33 @@
- form = local_assigns.fetch(:form)
.form-group
- = label_tag :merge_method_merge, class: 'label-bold' do
- = _('Merge method')
- .form-check
+ %b= s_('ProjectSettings|Merge method')
+ %p.text-secondary= s_('ProjectSettings|This will dictate the commit history when you merge a merge request')
+ .form-check.mb-2
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio form-check-input"
= label_tag :project_merge_method_merge, class: 'form-check-label' do
- .mb-3
- = _('Merge commit')
- .text-secondary
- = _('A merge commit is created for every merge, and merging is allowed as long as there are no conflicts.')
+ = s_('ProjectSettings|Merge commit')
+ .descr.text-secondary
+ = s_('ProjectSettings|Every merge creates a merge commit')
-.form-check
- = form.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio form-check-input"
- = label_tag :project_merge_method_rebase_merge, class: 'form-check-label' do
- .mb-3
- = _('Merge commit with semi-linear history')
- .text-secondary
- = _('A merge commit is created for every merge, but merging is only allowed if fast-forward merge is possible. This way you could make sure that if this merge request would build, after merging to target branch it would also build.')
- .text-secondary
- = _('When fast-forward merge is not possible, the user is given the option to rebase.')
+ .form-check.mb-2
+ = form.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio form-check-input"
+ = label_tag :project_merge_method_rebase_merge, class: 'form-check-label' do
+ = s_('ProjectSettings|Merge commit with semi-linear history')
+ .descr.text-secondary
+ = s_('ProjectSettings|Every merge creates a merge commit')
+ %br
+ = s_('ProjectSettings|Fast-forward merges only')
+ %br
+ = s_('ProjectSettings|When conflicts arise the user is given the option to rebase')
-.form-check
- = form.radio_button :merge_method, :ff, class: "js-merge-method-radio qa-radio-button-merge-ff form-check-input"
- = label_tag :project_merge_method_ff, class: 'form-check-label' do
- .mb-3
- = _('Fast-forward merge')
- .text-secondary
- = _('No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. When fast-forward merge is not possible, the user is given the option to rebase.')
+ .form-check.mb-2
+ = form.radio_button :merge_method, :ff, class: "js-merge-method-radio qa-radio-button-merge-ff form-check-input"
+ = label_tag :project_merge_method_ff, class: 'form-check-label' do
+ = s_('ProjectSettings|Fast-forward merge')
+ .descr.text-secondary
+ = s_('ProjectSettings|No merge commits are created')
+ %br
+ = s_('ProjectSettings|Fast-forward merges only')
+ %br
+ = s_('ProjectSettings|When conflicts arise the user is given the option to rebase')
diff --git a/app/views/projects/_merge_request_merge_options_settings.html.haml b/app/views/projects/_merge_request_merge_options_settings.html.haml
new file mode 100644
index 00000000000..5ab475822de
--- /dev/null
+++ b/app/views/projects/_merge_request_merge_options_settings.html.haml
@@ -0,0 +1,14 @@
+- form = local_assigns.fetch(:form)
+
+.form-group
+ %b= s_('ProjectSettings|Merge options')
+ %p.text-secondary= s_('ProjectSettings|Additional merge request capabilities that influence how and when merges will be performed')
+ = render_if_exists 'projects/merge_pipelines_settings', form: form
+ .form-check.mb-2
+ = form.check_box :resolve_outdated_diff_discussions, class: 'form-check-input'
+ = form.label :resolve_outdated_diff_discussions, class: 'form-check-label' do
+ = s_('ProjectSettings|Automatically resolve merge request diff discussions when they become outdated')
+ .form-check.mb-2
+ = form.check_box :printing_merge_request_link_enabled, class: 'form-check-input'
+ = form.label :printing_merge_request_link_enabled, class: 'form-check-label' do
+ = s_('ProjectSettings|Show link to create/view merge request when pushing from the command line')
diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml
deleted file mode 100644
index 3a9f7ca42db..00000000000
--- a/app/views/projects/_merge_request_merge_settings.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-- form = local_assigns.fetch(:form)
-
-.form-group
- .form-check.builds-feature{ class: ("hidden" if @project && @project.project_feature.send(:builds_access_level) == 0) }
- = form.check_box :only_allow_merge_if_pipeline_succeeds, class: 'form-check-input'
- = form.label :only_allow_merge_if_pipeline_succeeds, class: 'form-check-label' do
- .mb-3
- = _('Only allow merge requests to be merged if the pipeline succeeds')
- .text-secondary
- = _('Pipelines need to be configured to enable this feature.')
- = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds'), target: '_blank'
- = render_if_exists 'projects/merge_pipelines_settings', form: form
- .form-check
- = form.check_box :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-input'
- = form.label :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-label' do
- %p= _('Only allow merge requests to be merged if all discussions are resolved')
- .form-check
- = form.check_box :resolve_outdated_diff_discussions, class: 'form-check-input'
- = form.label :resolve_outdated_diff_discussions, class: 'form-check-label' do
- %p= _('Automatically resolve merge request diff discussions when they become outdated')
- .form-check
- = form.check_box :printing_merge_request_link_enabled, class: 'form-check-input'
- = form.label :printing_merge_request_link_enabled, class: 'form-check-label' do
- %p= _('Show link to create/view merge request when pushing from the command line')
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index c80e831dd33..f2ba38387a3 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -2,4 +2,6 @@
= render 'projects/merge_request_merge_method_settings', project: @project, form: form
-= render 'projects/merge_request_merge_settings', form: form
+= render 'projects/merge_request_merge_options_settings', project: @project, form: form
+
+= render 'projects/merge_request_merge_checks_settings', project: @project, form: form
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 1a3e4a5d608..c04530dc62c 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -27,7 +27,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
- %p= _('Choose your merge method, set up a default merge request description template.')
+ %p= _('Choose your merge method, options, checks, and set up a default merge request description template.')
.settings-content
= render_if_exists 'shared/promotions/promote_mr_features'
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 2c95ac6dbb3..4bf1d8702af 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -77,7 +77,7 @@
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago')
- #js-related-merge-requests{ data: { endpoint: api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid), project_namespace: @project.namespace.path, project_path: @project.path } }
+ #js-related-merge-requests{ data: { endpoint: expose_url(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
- if can?(current_user, :download_code, @project)
#related-branches{ data: { url: related_branches_project_issue_path(@project, @issue) } }
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index ff7c36c2d5b..95027634de2 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -48,7 +48,12 @@
= form_for @project, html: { class: 'new_project' } do |f|
= render 'new_project_fields', f: f, project_name_id: "blank-project-name"
- .tab-pane.no-padding{ id: 'create-from-template-pane', class: active_when(active_tab == 'template'), role: 'tabpanel' }
+ #create-from-template-pane.tab-pane.px-0.pb-0{ class: active_when(active_tab == 'template'), role: 'tabpanel' }
+ .card-slim.m-4.p-4
+ %div
+ - contributing_templates_url = 'https://gitlab.com/gitlab-org/project-templates/contributing'
+ - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: contributing_templates_url }
+ = _('Learn how to %{link_start}contribute to the built-in templates%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= form_for @project, html: { class: 'new_project' } do |f|
.project-template
.form-group
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 6651f12f6de..9d1648fbf70 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,13 +1,11 @@
- subject = local_assigns[:subject]
- force_priority = local_assigns.fetch(:force_priority, false)
-- show_label_issues_link = defined?(@project) && show_label_issuables_link?(label, :issues, project: @project)
-- show_label_merge_requests_link = defined?(@project) && show_label_issuables_link?(label, :merge_requests, project: @project)
+- subject_or_group_defined = defined?(@project) || defined?(@group)
+- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project)
+- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests, project: @project)
.label-name
- - if defined?(@project)
- = link_to_label(label, subject: @project, tooltip: false)
- - else
- = render_label(label, tooltip: false)
+ = render_label(label, tooltip: false)
.label-description
.append-right-default.prepend-left-default
- if label.description.present?
@@ -22,5 +20,6 @@
%li.label-link-item.inline
= link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') }
- if force_priority
+ &middot;
%li.label-link-item.priority-badge.js-priority-badge.inline.prepend-left-10
.label-badge.label-badge-blue= _('Prioritized label')
diff --git a/app/views/shared/boards/components/sidebar/_due_date.html.haml b/app/views/shared/boards/components/sidebar/_due_date.html.haml
index 5630375f428..117d56b30f5 100644
--- a/app/views/shared/boards/components/sidebar/_due_date.html.haml
+++ b/app/views/shared/boards/components/sidebar/_due_date.html.haml
@@ -7,7 +7,7 @@
.value
.value-content
%span.no-value{ "v-if" => "!issue.dueDate" }
- = _("No due date")
+ = _("None")
%span.bold{ "v-if" => "issue.dueDate" }
{{ issue.dueDate | due-date }}
- if can_admin_issue?
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 0798b1da4b7..d4be7289a49 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -73,7 +73,7 @@
%span.bold= issuable_sidebar[:due_date].to_s(:medium)
- else
%span.no-value
- = _('No due date')
+ = _('None')
- if can_edit_issuable
%span.no-value.js-remove-due-date-holder{ class: ("hidden" if issuable_sidebar[:due_date].nil?) }
\-
diff --git a/app/workers/cluster_configure_worker.rb b/app/workers/cluster_configure_worker.rb
index b984dee5b21..22681157b62 100644
--- a/app/workers/cluster_configure_worker.rb
+++ b/app/workers/cluster_configure_worker.rb
@@ -5,10 +5,10 @@ class ClusterConfigureWorker
include ClusterQueue
def perform(cluster_id)
- return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
-
Clusters::Cluster.find_by_id(cluster_id).try do |cluster|
- Clusters::RefreshService.create_or_update_namespaces_for_cluster(cluster)
+ if cluster.project_type? || Feature.disabled?(:ci_preparing_state, default_enabled: true)
+ Clusters::RefreshService.create_or_update_namespaces_for_cluster(cluster)
+ end
end
end
end
diff --git a/app/workers/cluster_project_configure_worker.rb b/app/workers/cluster_project_configure_worker.rb
index d7bea69a01c..497e57c0d0b 100644
--- a/app/workers/cluster_project_configure_worker.rb
+++ b/app/workers/cluster_project_configure_worker.rb
@@ -5,8 +5,6 @@ class ClusterProjectConfigureWorker
include ClusterQueue
def perform(project_id)
- return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
-
project = Project.find(project_id)
::Clusters::RefreshService.create_or_update_namespaces_for_project(project)
diff --git a/changelogs/unreleased/10012-move-ee-diff-for-boards-issue-card-inner.yml b/changelogs/unreleased/10012-move-ee-diff-for-boards-issue-card-inner.yml
new file mode 100644
index 00000000000..f15375e83f4
--- /dev/null
+++ b/changelogs/unreleased/10012-move-ee-diff-for-boards-issue-card-inner.yml
@@ -0,0 +1,5 @@
+---
+title: Move ee-specific code from boards/components/issue_card_inner.vue
+merge_request: 27394
+author: Roman Rodionov
+type: other
diff --git a/changelogs/unreleased/57815.yml b/changelogs/unreleased/57815.yml
new file mode 100644
index 00000000000..ccf76c99f1e
--- /dev/null
+++ b/changelogs/unreleased/57815.yml
@@ -0,0 +1,5 @@
+---
+title: Added Omniauth UltraAuth strategy to GitLab
+merge_request:
+author: Kartikey Tanna
+type: added
diff --git a/changelogs/unreleased/58293-extract-discussion-actions.yml b/changelogs/unreleased/58293-extract-discussion-actions.yml
new file mode 100644
index 00000000000..2ca4716a6de
--- /dev/null
+++ b/changelogs/unreleased/58293-extract-discussion-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Extract DiscussionActions component from NoteableDiscussion
+merge_request: 27227
+author:
+type: other
diff --git a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml b/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
new file mode 100644
index 00000000000..df6e6ea4be3
--- /dev/null
+++ b/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
@@ -0,0 +1,5 @@
+---
+title: Disable just-in-time Kubernetes resource creation for project level clusters
+merge_request: 27352
+author:
+type: changed
diff --git a/changelogs/unreleased/add-runner-access-level-registration.yml b/changelogs/unreleased/add-runner-access-level-registration.yml
new file mode 100644
index 00000000000..7ae95025abb
--- /dev/null
+++ b/changelogs/unreleased/add-runner-access-level-registration.yml
@@ -0,0 +1,5 @@
+---
+title: Add option to set access_level of runners upon registration
+merge_request: 27490
+author: Zelin L
+type: added
diff --git a/changelogs/unreleased/an-use-labkit.yml b/changelogs/unreleased/an-use-labkit.yml
new file mode 100644
index 00000000000..ab293c15787
--- /dev/null
+++ b/changelogs/unreleased/an-use-labkit.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate correlation and tracing code to LabKit
+merge_request: 25379
+author:
+type: other
diff --git a/changelogs/unreleased/ce-10725-restructure-project-merge-request-settings-page.yml b/changelogs/unreleased/ce-10725-restructure-project-merge-request-settings-page.yml
new file mode 100644
index 00000000000..7aa9204fe4e
--- /dev/null
+++ b/changelogs/unreleased/ce-10725-restructure-project-merge-request-settings-page.yml
@@ -0,0 +1,5 @@
+---
+title: Reorganize project merge request settings
+merge_request: 26834
+author:
+type: changed
diff --git a/changelogs/unreleased/gitlab-issue-54894.yml b/changelogs/unreleased/gitlab-issue-54894.yml
new file mode 100644
index 00000000000..513c0163c0e
--- /dev/null
+++ b/changelogs/unreleased/gitlab-issue-54894.yml
@@ -0,0 +1,6 @@
+---
+title: Fix some label links not appearing on group labels page and label title being
+ a link on project labels page
+merge_request: 24060
+author: Tanya Pazitny
+type: fixed
diff --git a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml b/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
new file mode 100644
index 00000000000..c1669a484aa
--- /dev/null
+++ b/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Gitaly to 1.34.0
+merge_request: 27494
+author:
+type: fixed
diff --git a/changelogs/unreleased/knative-0-5.yml b/changelogs/unreleased/knative-0-5.yml
new file mode 100644
index 00000000000..c7112b957e9
--- /dev/null
+++ b/changelogs/unreleased/knative-0-5.yml
@@ -0,0 +1,5 @@
+---
+title: Knative version bump 0.3 -> 0.5
+merge_request:
+author: Chris Baumbauer <cab@cabnetworks.net>
+type: changed
diff --git a/changelogs/unreleased/markdown-autocomplete-escaping.yml b/changelogs/unreleased/markdown-autocomplete-escaping.yml
new file mode 100644
index 00000000000..0ea034b14ee
--- /dev/null
+++ b/changelogs/unreleased/markdown-autocomplete-escaping.yml
@@ -0,0 +1,5 @@
+---
+title: Only escape Markdown emphasis characters in autocomplete when necessary
+merge_request: 27457
+author:
+type: changed
diff --git a/changelogs/unreleased/rename_auto_deploy_app_links.yml b/changelogs/unreleased/rename_auto_deploy_app_links.yml
new file mode 100644
index 00000000000..c56b5fb5e5c
--- /dev/null
+++ b/changelogs/unreleased/rename_auto_deploy_app_links.yml
@@ -0,0 +1,5 @@
+---
+title: Move location of charts/auto-deploy-app -> gitlab-org/charts/auto-deploy-app
+merge_request: 27477
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-add-gitaly-backtrace.yml b/changelogs/unreleased/sh-add-gitaly-backtrace.yml
new file mode 100644
index 00000000000..1515edd6db9
--- /dev/null
+++ b/changelogs/unreleased/sh-add-gitaly-backtrace.yml
@@ -0,0 +1,5 @@
+---
+title: Add backtrace to Gitaly performance bar
+merge_request: 27345
+author:
+type: other
diff --git a/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml b/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml
new file mode 100644
index 00000000000..eb8774d652f
--- /dev/null
+++ b/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml
@@ -0,0 +1,5 @@
+---
+title: Bring back Rugged implementation of ListCommitsByOid
+merge_request: 27441
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml b/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
new file mode 100644
index 00000000000..b5b6eb87650
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
@@ -0,0 +1,5 @@
+---
+title: Bump required Ruby version check to 2.5.3
+merge_request: 27495
+author:
+type: other
diff --git a/changelogs/unreleased/tc-raw-log-in-plaintext-mail.yml b/changelogs/unreleased/tc-raw-log-in-plaintext-mail.yml
new file mode 100644
index 00000000000..373c2847ef2
--- /dev/null
+++ b/changelogs/unreleased/tc-raw-log-in-plaintext-mail.yml
@@ -0,0 +1,5 @@
+---
+title: Include link to raw job log in plain-text emails
+merge_request: 27409
+author:
+type: changed
diff --git a/changelogs/unreleased/weimeng-user-autocomplete-fix.yml b/changelogs/unreleased/weimeng-user-autocomplete-fix.yml
new file mode 100644
index 00000000000..aca9fc4be30
--- /dev/null
+++ b/changelogs/unreleased/weimeng-user-autocomplete-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Only show in autocomplete when author active
+merge_request: 27292
+author:
+type: fixed
diff --git a/changelogs/unreleased/xanf-gitlab-ce-add-template-link.yml b/changelogs/unreleased/xanf-gitlab-ce-add-template-link.yml
new file mode 100644
index 00000000000..b868758dcd2
--- /dev/null
+++ b/changelogs/unreleased/xanf-gitlab-ce-add-template-link.yml
@@ -0,0 +1,5 @@
+---
+title: Add instructions on how to contribute a Built-In template for project
+merge_request: 26976
+author:
+type: other
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index 5e790a9eccb..1ad93e14f7e 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -35,7 +35,7 @@ unless Sidekiq.server?
end
payload[:response] = event.payload[:response] if event.payload[:response]
- payload[Gitlab::CorrelationId::LOG_KEY] = Gitlab::CorrelationId.current_id
+ payload[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id
payload
end
diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb
index e051e5c68c4..eeb45fae753 100644
--- a/config/initializers/peek.rb
+++ b/config/initializers/peek.rb
@@ -19,7 +19,7 @@ Peek.into Peek::Views::Gitaly
Peek.into Peek::Views::Rblineprof
Peek.into Peek::Views::Redis
Peek.into Peek::Views::GC
-Peek.into Peek::Views::Tracing if Gitlab::Tracing.tracing_url_enabled?
+Peek.into Peek::Views::Tracing if Labkit::Tracing.tracing_url_enabled?
# rubocop:disable Naming/ClassAndModuleCamelCase
class PEEK_DB_CLIENT
diff --git a/config/initializers/tracing.rb b/config/initializers/tracing.rb
index ddd91150c90..3c8779f238f 100644
--- a/config/initializers/tracing.rb
+++ b/config/initializers/tracing.rb
@@ -1,16 +1,14 @@
# frozen_string_literal: true
-if Gitlab::Tracing.enabled?
- require 'opentracing'
-
+if Labkit::Tracing.enabled?
Rails.application.configure do |config|
- config.middleware.insert_after Gitlab::Middleware::CorrelationId, ::Gitlab::Tracing::RackMiddleware
+ config.middleware.insert_after Gitlab::Middleware::CorrelationId, ::Labkit::Tracing::RackMiddleware
end
# Instrument the Sidekiq client
Sidekiq.configure_client do |config|
config.client_middleware do |chain|
- chain.add Gitlab::Tracing::Sidekiq::ClientMiddleware
+ chain.add Labkit::Tracing::Sidekiq::ClientMiddleware
end
end
@@ -18,20 +16,20 @@ if Gitlab::Tracing.enabled?
if Sidekiq.server?
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
- chain.add Gitlab::Tracing::Sidekiq::ServerMiddleware
+ chain.add Labkit::Tracing::Sidekiq::ServerMiddleware
end
end
end
# Instrument Rails
- Gitlab::Tracing::Rails::ActiveRecordSubscriber.instrument
- Gitlab::Tracing::Rails::ActionViewSubscriber.instrument
+ Labkit::Tracing::Rails::ActiveRecordSubscriber.instrument
+ Labkit::Tracing::Rails::ActionViewSubscriber.instrument
# In multi-processed clustered architectures (puma, unicorn) don't
# start tracing until the worker processes are spawned. This works
# around issues when the opentracing implementation spawns threads
Gitlab::Cluster::LifecycleEvents.on_worker_start do
- tracer = Gitlab::Tracing::Factory.create_tracer(Gitlab.process_name, Gitlab::Tracing.connection_string)
+ tracer = Labkit::Tracing::Factory.create_tracer(Gitlab.process_name, Labkit::Tracing.connection_string)
OpenTracing.global_tracer = tracer if tracer
end
end
diff --git a/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml
index e88a8359f5e..3c67ca36791 100644
--- a/config/prometheus/common_metrics.yml
+++ b/config/prometheus/common_metrics.yml
@@ -209,6 +209,6 @@ panel_groups:
weight: 1
metrics:
- id: system_metrics_knative_function_invocation_count
- query_range: 'floor(sum(rate(istio_revision_request_count{destination_configuration="%{function_name}", destination_namespace="%{kube_namespace}"}[1m])*30))'
+ query_range: 'floor(sum(rate(istio_revision_request_count{destination_configuration="%{function_name}", destination_namespace="%{kube_namespace}"}[1m])/3))'
label: invocations / minute
unit: requests
diff --git a/db/migrate/20190408163745_prometheus_knative05_fix.rb b/db/migrate/20190408163745_prometheus_knative05_fix.rb
new file mode 100644
index 00000000000..c11f6f0e29b
--- /dev/null
+++ b/db/migrate/20190408163745_prometheus_knative05_fix.rb
@@ -0,0 +1,20 @@
+# 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 PrometheusKnative05Fix < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+ DOWNTIME = false
+
+ def up
+ Importers::CommonMetricsImporter.new.execute
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d1b3672725d..c0399529deb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20190326164045) do
+ActiveRecord::Schema.define(version: 20190408163745) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 0a950352ecf..951961e45ff 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -494,7 +494,9 @@ GET /projects/:id
"name": "Diaspora",
"path": "diaspora",
"kind": "group",
- "full_path": "diaspora"
+ "full_path": "diaspora",
+ "avatar_url": "http://localhost:3000/uploads/group/avatar/3/foo.jpg",
+ "web_url": "http://localhost:3000/groups/diaspora"
},
"import_status": "none",
"import_error": null,
@@ -561,6 +563,8 @@ GET /projects/:id
}
```
+**Note**: The `web_url` and `avatar_url` attributes on `namespace` were [introduced][ce-27427] in GitLab 11.11.
+
If the project is a fork, and you provide a valid token to authenticate, the
`forked_from_project` field will appear in the response.
@@ -1587,3 +1591,4 @@ GET /projects/:id/snapshot
[eep]: https://about.gitlab.com/pricing/ "Available only in GitLab Premium"
[ee-6137]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/6137
+[ce-27427]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27427
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 46f7b1d2a25..2d91428d1c1 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -486,6 +486,7 @@ POST /runners
| `locked` | boolean| no | Whether the Runner should be locked for current project |
| `run_untagged` | boolean | no | Whether the Runner should handle untagged jobs |
| `tag_list` | Array[String] | no | List of Runner's tags |
+| `access_level` | string | no | The access_level of the runner; `not_protected` or `ref_protected` |
| `maximum_timeout` | integer | no | Maximum timeout set when this Runner will handle the job |
```
diff --git a/doc/api/services.md b/doc/api/services.md
index e8ae7ff78f4..742abccb69e 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -545,7 +545,7 @@ GET /projects/:id/services/jira
Set JIRA service for a project.
> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
-> `project_url` are replaced by `project_key`, `url`. If you are using an
+> `project_url` are replaced by `url`. If you are using an
> older version, [follow this documentation][old-jira-api].
```
@@ -557,7 +557,7 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `url` | string | yes | The URL to the JIRA project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
-| `project_key` | string | yes | The short identifier for your JIRA project, all uppercase, e.g., `PROJ`. |
+| `api_url` | string | no | The base URL to the JIRA instance API. Web URL value will be used if not set. For example, `https://jira-api.example.com`. |
| `username` | string | yes | The username of the user created to be used with GitLab/JIRA. |
| `password` | string | yes | The password of the user created to be used with GitLab/JIRA. |
| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md
index 2de751c9e62..6a03ab910fc 100644
--- a/doc/ci/merge_request_pipelines/index.md
+++ b/doc/ci/merge_request_pipelines/index.md
@@ -69,7 +69,7 @@ when a merge request was created or updated. For example:
## Combined ref pipelines **[PREMIUM]**
-> [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
It's possible for your source and target branches to diverge, which can result
in the scenario that source branch's pipeline was green, the target's pipeline was green,
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 61d1a904f76..afd578e2621 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -136,12 +136,21 @@ The output will be:
![Output custom variable](img/custom_variable_output.png)
-CAUTION: **Important:**
-Be aware that variables are not masked, and their values can be shown
-in the job logs if explicitly asked to do so. If your project is public or
-internal, you can set the pipelines private from your [project's Pipelines
-settings](../../user/project/pipelines/settings.md#visibility-of-pipelines).
-Follow the discussion in issue [#13784][ce-13784] for masking the variables.
+### Masked Variables
+
+By default, variables will be created as masked variables.
+This means that the value of the variable will be hidden in job logs,
+though it must match certain requirements to do so:
+
+- The value must be a single line.
+- The value must not have escape characters.
+- The value must not use variables.
+- The value must not have any whitespace.
+- The value must be at least 8 characters long.
+
+If the value does not meet the requirements above, then the CI variable will fail to save.
+In order to save, either alter the value to meet the masking requirements
+or disable `Masked` for the variable.
### Syntax of environment variables in job scripts
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 36a0bf10416..2e85e34f17b 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -2472,18 +2472,18 @@ Use [`stage`](#stage) instead.
## Custom build directories
-> [Introduced][https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1267] in Gitlab Runner 11.10
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1267) in Gitlab Runner 11.10
NOTE: **Note:**
This can only be used when `custom_build_dir` is enabled in the [Runner's
configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnerscustom_build_dir-section).
This is the default configuration for `docker` and `kubernetes` executor.
-By default, GitLab Runner clones the repository in a unique subpath of the `$CI_BUILDS_DIR` directory.
-However, sometimes your project might require the code in a specific directory,
-but sometimes your project might require to have the code in a specific directory,
-like Go projects, for example. In that case, you can specify the `GIT_CLONE_PATH` variable
-to tell the Runner in which directory to clone the repository:
+By default, GitLab Runner clones the repository in a unique subpath of the
+`$CI_BUILDS_DIR` directory. However, your project might require the code in a
+specific directory (Go projects, for example). In that case, you can specify
+the `GIT_CLONE_PATH` variable to tell the Runner in which directory to clone the
+repository:
```yml
variables:
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 3a6f4bd8ed2..52a4bb27817 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -50,9 +50,7 @@ Adhere to the [Documentation Style Guide](styleguide.md). If a style standard is
## Folder structure and files
-Beyond the top-level directories under /doc, which mainly pertain to audiences (`user`, `administration`, `development`), we organize by product area and subject, not type.
-
-For complete details, see the [Content](styleguide.md#content) section of the [Documentation Style Guide](styleguide.md).
+See the [Structure](styleguide.md#structure) section of the [Documentation Style Guide](styleguide.md).
## Changing document location
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index d16a8243def..e36ad01d4f8 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -13,12 +13,9 @@ For programmatic help adhering to the guidelines, see [linting](index.md#linting
See the GitLab handbook for further [writing style guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines)
that apply to all GitLab content, not just documentation.
-## Content
+## Documentation is the single source of truth (SSOT)
-These guidelines help toward the goal of having every user's search of documentation
-yield a useful result, and ensuring content is consistent, helpful, and easy to consume.
-
-### Single source of truth (SSOT) on the GitLab product
+### Why a single source of truth
The documentation is the SSOT for all information related to the implementation, usage, and troubleshooting of GitLab products and features. It evolves continually, in keeping with new products and features, and with improvements for clarity, accuracy, and completeness.
@@ -26,31 +23,43 @@ This policy prevents information silos, ensuring that it remains easy to find in
It also informs decisions about the kinds of content we include in our documentation.
-### All helpful information
+The documentation is a continually evolving SSOT for all information related to the implementation, usage, and troubleshooting of GitLab products and features.
+
+### All information
Include problem-solving actions that may address rare cases or be considered 'risky', so long as proper context is provided in the form of fully detailed warnings and caveats. This kind of content should be included as it could be helpful to others and, when properly explained, its benefits outweigh the risks. If you think you have found an exception to this rule, contact the Technical Writing team.
-### All helpful media types and sources
+We will add all troubleshooting information to the documentation, no matter how unlikely a user is to encounter a situation.
+For the Troubleshooting sections, people in GitLab Support can merge additions themselves.
+
+### All media types
Include any media types/sources if the content is relevant to readers. You can freely include or link presentations, diagrams, videos, etc.; no matter who it was originally composed for, if it is helpful to any of our audiences, we can include it.
- If you use an image that has a separate source file (for example, a vector or diagram format), link the image to the source file so that it may be reused or updated by anyone.
- Do not copy and paste content from other sources unless it is a limited quotation with the source cited. Typically it is better to either rephrase relevant information in your own words or link out to the other source.
+
+### No special types
-### Markdown
+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/):
-All GitLab documentation is written using [Markdown](https://en.wikipedia.org/wiki/Markdown).
+1. Tutorials
+2. How-to guides
+3. Explanation
+4. Reference (for example, a glossary)
-The [documentation website](https://docs.gitlab.com) uses GitLab Kramdown as its Markdown rendering engine. For a complete Kramdown reference, see the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+At GitLab, we have so many product changes in our monthly releases that we can't afford to continually update multiple types of information.
+If we have multiple types, the information will become outdated. Therefore, we have a [single template](structure.md) for documentation.
-The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown)
-Ruby gem will support all [GFM markup](../../user/markdown.md) in the future. That is,
-all markup that is supported for display in the GitLab application itself. For now,
-use regular Markdown markup, following the rules in the linked style guide.
+We currently do not distinguish specific document types, although we are open to reconsidering this policy
+once the documentation has reached a future stage of maturity and quality. If you are reading this, then despite our
+continual improvement efforts, that point hasn't been reached.
-Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help).
+### Link instead of summarize
-## Structure
+There is a temptation to summarize the information on another page.
+This will cause the information to live in two places.
+Instead, link to the SSOT and explain why it is important to consume the information.
### Organize by topic, not by type
@@ -63,6 +72,40 @@ it difficult to browse for the information you need and difficult to maintain up
Instead, organize content by its subject (e.g. everything related to CI goes together)
and cross-link between any related content.
+### Docs-first methodology
+
+We employ a **docs-first methodology** to help ensure that the docs remain a complete and trusted resource, and to make communicating about the use of GitLab more efficient.
+
+* If the answer to a question exists in documentation, share the link to the docs instead of rephrasing the information.
+* When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information.
+
+New information that would be useful toward the future usage or troubleshooting of GitLab should not be written directly in a forum or other messaging system, but added to a docs MR and then referenced, as described above. Note that among any other doc changes, you can always add a Troubleshooting section to a doc if none exists, or un-comment and use the placeholder Troubleshooting section included as part of our [doc template](structure.md#template-for-new-docs), if present.
+
+The more we reflexively add useful information to the docs, the more (and more successfully) the docs will be used to efficiently accomplish tasks and solve problems.
+
+If you have questions when considering, authoring, or editing docs, ask the Technical Writing team on Slack in `#docs` or in GitLab by mentioning the writer for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). Otherwise, forge ahead with your best effort. It does not need to be perfect; the team is happy to review and improve upon your content. Please review the [Documentation guidelines](index.md) before you begin your first documentation MR.
+
+Having a knowledge base is any form that is separate from the documentation would be against the docs-first methodology because the content would overlap with the documentation.
+
+## Markdown
+
+All GitLab documentation is written using [Markdown](https://en.wikipedia.org/wiki/Markdown).
+
+The [documentation website](https://docs.gitlab.com) uses GitLab Kramdown as its Markdown rendering engine. For a complete Kramdown reference, see the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+
+The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown)
+Ruby gem will support all [GFM markup](../../user/markdown.md) in the future. That is,
+all markup that is supported for display in the GitLab application itself. For now,
+use regular Markdown markup, following the rules in the linked style guide.
+
+Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help).
+
+## Structure
+
+### Organize by topic, not by type
+
+Because we want documentation to be a SSOT, we should [organize by topic, not by type](#organize-by-topic-not-by-type).
+
### Folder structure overview
The documentation is separated by top-level audience folders [`user`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/user),
@@ -133,7 +176,7 @@ changes, regardless, and can move content if there is a better place for it.
### Avoid duplication
-Do not include the same information in multiple places. Instead, choose one single-source-of-truth location and link from other relevant locations.
+Do not include the same information in multiple places. [Link to a SSOT instead.](#link-instead-of-summarize)
### References across documents
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index bfde26dbe4a..d7b6b6aaae5 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -80,6 +80,8 @@ most commonly-used RPCs can be enabled via feature flags:
* `rugged_get_tree_entries`
* `rugged_tree_entry`
* `rugged_commit_is_ancestor`
+* `rugged_commit_tree_entry`
+* `rugged_list_commits_by_oid`
A convenience Rake task can be used to enable or disable these flags
all together. To enable:
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 352651fe91b..1fa6e38ea5a 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -4,6 +4,13 @@
_This diagram demonstrates the relative priority of each test type we use. `e2e` stands for end-to-end._
+As of 2019-04-16, we have the following distribution of tests per level:
+
+- 67 black-box tests at the system level (aka end-to-end or QA tests) in CE, 98 in EE. This represents 0.3% of all the CE tests (0.3% in EE).
+- 5,457 white-box tests at the system level (aka system or feature tests) in CE, 6,585 in EE. This represents 24.6% of all the CE tests (20.3% in EE).
+- 8,298 integration tests in CE, 10,633 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.2% of all the CE tests (32.8% in EE).
+- 8,403 unit tests in CE, 15,090 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.8% of all the CE tests (46.6% in EE).
+
## Unit tests
Formal definition: <https://en.wikipedia.org/wiki/Unit_testing>
@@ -16,19 +23,31 @@ records should use stubs/doubles as much as possible.
| Code path | Tests path | Testing engine | Notes |
| --------- | ---------- | -------------- | ----- |
+| `app/assets/javascripts/` | `spec/javascripts/`, `spec/frontend/` | Karma & Jest | More details in the [Frontend Testing guide](frontend_testing.md) section. |
| `app/finders/` | `spec/finders/` | RSpec | |
+| `app/graphql/` | `spec/graphql/` | RSpec | |
| `app/helpers/` | `spec/helpers/` | RSpec | |
-| `app/db/{post_,}migrate/` | `spec/migrations/` | RSpec | More details at [`spec/migrations/README.md`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md). |
+| `app/models/` | `spec/models/` | RSpec | |
| `app/policies/` | `spec/policies/` | RSpec | |
| `app/presenters/` | `spec/presenters/` | RSpec | |
-| `app/routing/` | `spec/routing/` | RSpec | |
| `app/serializers/` | `spec/serializers/` | RSpec | |
| `app/services/` | `spec/services/` | RSpec | |
-| `app/tasks/` | `spec/tasks/` | RSpec | |
| `app/uploaders/` | `spec/uploaders/` | RSpec | |
+| `app/validators/` | `spec/validators/` | RSpec | |
| `app/views/` | `spec/views/` | RSpec | |
| `app/workers/` | `spec/workers/` | RSpec | |
-| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [Frontend Testing guide](frontend_testing.md) section. |
+| `bin/` | `spec/bin/` | RSpec | |
+| `config/` | `spec/config/` | RSpec | |
+| `config/initializers/` | `spec/initializers/` | RSpec | |
+| `config/routes.rb`, `config/routes/` | `spec/routing/` | RSpec | |
+| `config/puma.example.development.rb`, `config/unicorn.rb.example` | `spec/rack_servers/` | RSpec | |
+| `db/` | `spec/db/` | RSpec | |
+| `db/{post_,}migrate/` | `spec/migrations/` | RSpec | More details at [`spec/migrations/README.md`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md). |
+| `Gemfile` | `spec/dependencies/`, `spec/sidekiq/` | RSpec | |
+| `lib/` | `spec/lib/` | RSpec | |
+| `lib/tasks/` | `spec/tasks/` | RSpec | |
+| `rubocop/` | `spec/rubocop/` | RSpec | |
+| `spec/factories` | `spec/factories_spec.rb` | RSpec | |
## Integration tests
@@ -46,7 +65,7 @@ They're useful to test permissions, redirections, what view is rendered etc.
| `app/mailers/` | `spec/mailers/` | RSpec | |
| `lib/api/` | `spec/requests/api/` | RSpec | |
| `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | |
-| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [Karma JavaScript test suite](frontend_testing.md#karma-test-suite) section. |
+| `app/assets/javascripts/` | `spec/javascripts/`, `spec/frontend/` | Karma & Jest | More details in the [Frontend Testing guide](frontend_testing.md) section. |
### About controller tests
diff --git a/doc/integration/img/ultra_auth_credentials.png b/doc/integration/img/ultra_auth_credentials.png
new file mode 100644
index 00000000000..cff98a4b056
--- /dev/null
+++ b/doc/integration/img/ultra_auth_credentials.png
Binary files differ
diff --git a/doc/integration/img/ultra_auth_edit_callback_url.png b/doc/integration/img/ultra_auth_edit_callback_url.png
new file mode 100644
index 00000000000..b7548122c5e
--- /dev/null
+++ b/doc/integration/img/ultra_auth_edit_callback_url.png
Binary files differ
diff --git a/doc/integration/img/ultra_auth_edit_callback_url_highlighted.png b/doc/integration/img/ultra_auth_edit_callback_url_highlighted.png
new file mode 100644
index 00000000000..4abf224756c
--- /dev/null
+++ b/doc/integration/img/ultra_auth_edit_callback_url_highlighted.png
Binary files differ
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 2932c884d04..7fd39b02fbe 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -33,6 +33,7 @@ contains some settings that are common for all providers.
- [Authentiq](../administration/auth/authentiq.md)
- [OAuth2Generic](oauth2_generic.md)
- [JWT](../administration/auth/jwt.md)
+- [UltraAuth](ultra_auth.md)
## Initial OmniAuth Configuration
diff --git a/doc/integration/ultra_auth.md b/doc/integration/ultra_auth.md
new file mode 100644
index 00000000000..139cca456aa
--- /dev/null
+++ b/doc/integration/ultra_auth.md
@@ -0,0 +1,78 @@
+# UltraAuth OmniAuth Provider
+
+You can integrate your GitLab instance with [UltraAuth](https://ultraauth.com) to enable users to perform secure biometric authentication to your GitLab instance with your UltraAuth account. Users have to perform the biometric authentication using their mobile device with fingerprint sensor.
+
+## Create UltraAuth Application
+
+To enable UltraAuth OmniAuth provider, you must use UltraAuth's credentials for your GitLab instance.
+To get the credentials (a pair of Client ID and Client Secret), you must register an application on UltraAuth.
+
+1. Sign in to [UltraAuth](https://ultraauth.com).
+1. Navigate to [Create an App](https://ultraauth.com/select-strategy) and click on "Ruby on Rails".
+1. Scroll down the page that is displayed to locate the **Client ID** and **Client Secret**.
+ Keep this page open as you continue configuration.
+ ![UltraAuth Credentials: OPENID_CLIENT_ID and OPENID_CLIENT_SECRET](img/ultra_auth_credentials.png)
+1. Click on "Edit Callback URL" link.
+ ![Edit UltraAuth Callback URL](img/ultra_auth_edit_callback_url_highlighted.png)
+1. The callback URL will be `http(s)://<your_domain>/users/auth/ultraauth/callback`
+ ![UltraAuth Callback URL](img/ultra_auth_edit_callback_url.png)
+1. Select **Register application**.
+1. On your GitLab server, open the configuration file.
+
+ For omnibus package:
+
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For installations from source:
+
+ ```sh
+ cd /home/git/gitlab
+ sudo -u git -H editor config/gitlab.yml
+ ```
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+1. Add the provider configuration:
+
+ For omnibus package:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ "name" => "ultraauth",
+ "app_id" => "OPENID_CLIENT_ID",
+ "app_secret" => "OPENID_CLIENT_SECRET",
+ "args" => {
+ "client_options" => {
+ "redirect_uri" => "https://example.com/users/auth/ultraauth/callback"
+ }
+ }
+ }
+ ]
+ ```
+
+ For installation from source:
+
+ ```
+ - { name: 'ultraauth',
+ app_id: 'OPENID_CLIENT_ID',
+ app_secret: 'OPENID_CLIENT_SECRET',
+ args: {
+ client_options: {
+ redirect_uri: 'https://example.com/users/auth/ultraauth/callback'
+ }
+ }
+ }
+ ```
+ __Replace `https://example.com/users/auth/ultraauth/callback` with your application's Callback URL.__
+1. Change `OPENID_CLIENT_ID` to the Client ID from the UltraAuth application page.
+1. Change `OPENID_CLIENT_SECRET` to the Client Secret from the UltraAuth application page.
+1. Save the configuration file.
+1. [Reconfigure GitLab]( ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure ) or [restart GitLab]( ../administration/restart_gitlab.md#installations-from-source ) for the changes to take effect if you
+ installed GitLab via Omnibus or from source respectively.
+
+On the sign in page, there should now be a UltraAuth icon below the regular sign in form.
+Click the icon to begin the authentication process. UltraAuth will ask the user to sign in and authorize the GitLab application.
+If everything goes well, the user will be returned to GitLab and will be signed in.
+
+**Note:** GitLab requires the email address of each new user. Once the user is logged in using UltraAuth, GitLab will redirect the user to the profile page where they will have to provide the email and verify the email.
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 1d517a65ce2..0ab9406c681 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -460,7 +460,7 @@ for example after the merge request is merged, the Review App will automatically
be deleted.
Review apps are deployed using the
-[auto-deploy-app](https://gitlab.com/charts/auto-deploy-app) chart with
+[auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with
Helm. The app will be deployed into the [Kubernetes
namespace](../../user/project/clusters/index.md#deployment-variables)
for the environment.
@@ -528,7 +528,7 @@ You can make use of [environment variables](#environment-variables) to automatic
scale your pod replicas.
Apps are deployed using the
-[auto-deploy-app](https://gitlab.com/charts/auto-deploy-app) chart with
+[auto-deploy-app](https://gitlab.com/gitlab-org/charts/auto-deploy-app) chart with
Helm. The app will be deployed into the [Kubernetes
namespace](../../user/project/clusters/index.md#deployment-variables)
for the environment.
@@ -667,7 +667,7 @@ repo or by specifying a project variable:
- **Bundled chart** - If your project has a `./chart` directory with a `Chart.yaml`
file in it, Auto DevOps will detect the chart and use it instead of the [default
- one](https://gitlab.com/charts/auto-deploy-app).
+ one](https://gitlab.com/gitlab-org/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
- **Project variable** - Create a [project variable](../../ci/variables/README.md#gitlab-cicd-environment-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use or create two project variables `AUTO_DEVOPS_CHART_REPOSITORY` with the URL of a custom chart repository and `AUTO_DEVOPS_CHART` with the path to the chart.
@@ -735,7 +735,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| **Variable** | **Description** |
| ------------ | --------------- |
| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-base-domain). By default, set automatically by the [Auto DevOps setting](#enablingdisabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
-| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). |
+| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. This takes precedence over `REPLICAS`; defaults to 1. |
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
new file mode 100644
index 00000000000..72e76ac2a84
--- /dev/null
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -0,0 +1,112 @@
+# External authorization control
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/4216) in
+> [GitLab Premium](https://about.gitlab.com/pricing) 10.6.
+> [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/27056) to
+> [GitLab Core](https://about.gitlab.com/pricing/) in 11.10.
+
+In highly controlled environments, it may be necessary for access policy to be
+controlled by an external service that permits access based on project
+classification and user access. GitLab provides a way to check project
+authorization with your own defined service.
+
+## Overview
+
+Once the external service is configured and enabled, when a project is accessed,
+a request is made to the external service with the user information and project
+classification label assigned to the project. When the service replies with a
+known response, the result is cached for 6 hours.
+
+If the external authorization is enabled, GitLab will further block pages and
+functionality that render cross-project data. That includes:
+
+- most pages under Dashboard (Activity, Milestones, Snippets, Assigned merge
+ requests, Assigned issues, Todos)
+- under a specific group (Activity, Contribution analytics, Issues, Issue boards,
+ Labels, Milestones, Merge requests)
+- Global and Group search will be disabled
+
+This is to prevent performing to many requests at once to the external
+authorization service.
+
+Whenever access is granted or denied this is logged in a logfile called
+`external-policy-access-control.log`.
+Read more about logs GitLab keeps in the [omnibus documentation][omnibus-log-docs].
+
+## Configuration
+
+The external authorization service can be enabled by an admin on the GitLab's
+admin area under the settings page:
+
+![Enable external authorization service](img/external_authorization_service_settings.png)
+
+The available required properties are:
+
+- **Service URL**: The URL to make authorization requests to. When leaving the
+ URL blank, cross project features will remain available while still being able
+ to specify classification labels for projects.
+- **External authorization request timeout**: The timeout after which an
+ authorization request is aborted. When a request times out, access is denied
+ to the user.
+- **Client authentication certificate**: The certificate to use to authenticate
+ with the external authorization service.
+- **Client authentication key**: Private key for the certificate when
+ authentication is required for the external authorization service, this is
+ encrypted when stored.
+- **Client authentication key password**: Passphrase to use for the private key when authenticating with the external service this is encrypted when stored.
+- **Default classification label**: The classification label to use when
+ requesting authorization if no specific label is defined on the project
+
+When using TLS Authentication with a self signed certificate, the CA certificate
+needs to be trused by the openssl installation. When using GitLab installed using
+Omnibus, learn to install a custom CA in the
+[omnibus documentation][omnibus-ssl-docs]. Alternatively learn where to install
+custom certificates using `openssl version -d`.
+
+## How it works
+
+When GitLab requests access, it will send a JSON POST request to the external
+service with this body:
+
+```json
+{
+ "user_identifier": "jane@acme.org",
+ "project_classification_label": "project-label",
+ "user_ldap_dn": "CN=Jane Doe,CN=admin,DC=acme"
+}
+```
+
+The `user_ldap_dn` is optional and is only sent when the user is logged in
+through LDAP.
+
+When the external authorization service responds with a status code 200, the
+user is granted access. When the external service responds with a status code
+401 or 403, the user is denied access. In any case, the request is cached for 6 hours.
+
+When denying access, a `reason` can be optionally specified in the JSON body:
+
+```json
+{
+ "reason": "You are not allowed access to this project."
+}
+```
+
+Any other status code than 200, 401 or 403 will also deny access to the user, but the
+response will not be cached.
+
+If the service times out (after 500ms), a message "External Policy Server did
+not respond" will be displayed.
+
+## Classification labels
+
+You can use your own classification label in the project's
+**Settings > General > General project settings** page in the "Classification
+label" box. When no classification label is specified on a project, the default
+label defined in the [global settings](#configuration) will be used.
+
+The label will be shown on all project pages in the upper right corner.
+
+![classification label on project page](img/classification_label_on_project_page.png)
+
+[omnibus-ssl-docs]: https://docs.gitlab.com/omnibus/settings/ssl.html
+[omnibus-log-docs]: https://docs.gitlab.com/omnibus/settings/logs.html
diff --git a/doc/user/admin_area/settings/img/classification_label_on_project_page.png b/doc/user/admin_area/settings/img/classification_label_on_project_page.png
new file mode 100644
index 00000000000..4aedb332cec
--- /dev/null
+++ b/doc/user/admin_area/settings/img/classification_label_on_project_page.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/external_authorization_service_settings.png b/doc/user/admin_area/settings/img/external_authorization_service_settings.png
new file mode 100644
index 00000000000..9b8658fd1a1
--- /dev/null
+++ b/doc/user/admin_area/settings/img/external_authorization_service_settings.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index bf41fdecd10..c7e8bb5b33b 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -348,19 +348,23 @@ Custom commit messages will be introduced by
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53310) in GitLab 11.10.
-Reviewers can also suggest changes to
-multiple lines with a single suggestion within Merge Request diff discussions.
+Reviewers can also suggest changes to multiple lines with a single suggestion
+within Merge Request diff discussions by adjusting the range offsets. The
+offsets are relative to the position of the diff discussion, and specify the
+range to be replaced by the suggestion when it is applied.
![Multi-line suggestion syntax](img/multi-line-suggestion-syntax.png)
-In the example above, the suggestion covers three lines above and four lines below the commented diff line.
-It'd change from 3 lines _above_ to 4 lines _below_ the commented Diff line.
+In the example above, the suggestion covers three lines above and four lines
+below the commented line. When applied, it would replace from 3 lines _above_
+to 4 lines _below_ the commented line, with the suggested change.
![Multi-line suggestion preview](img/multi-line-suggestion-preview.png)
NOTE: **Note:**
-Suggestions covering multiple lines are limited to 100 lines _above_ and 100 lines _below_
-the commented diff line, allowing up to 200 changed lines per suggestion.
+Suggestions covering multiple lines are limited to 100 lines _above_ and 100
+lines _below_ the commented diff line, allowing up to 200 changed lines per
+suggestion.
## Start a discussion by replying to a standard comment
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 1983513174c..ccd60b9761f 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -565,7 +565,9 @@ service account of the cluster integration.
### Troubleshooting failed deployment jobs
GitLab will create a namespace and service account specifically for your
-deployment jobs, immediately before the jobs starts.
+deployment jobs. On project level clusters, this happens when the cluster
+is created. On group level clusters, resources are created immediately
+before the deployment job starts.
However, sometimes GitLab can not create them. In such instances, your job will fail with the message:
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 5b7e9ef906f..dfbed0ec95f 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -158,21 +158,6 @@ Follow these steps to deploy a function using the Node.js runtime to your Knativ
description: "node.js runtime function"
environment:
MY_FUNCTION: echo-js
-
- echo-rb:
- handler: MyEcho.my_function
- source: ./echo-rb
- runtime: https://gitlab.com/gitlab-org/serverless/runtimes/ruby
- description: "Ruby runtime function"
- environment:
- MY_FUNCTION: echo-rb
-
- echo-docker:
- handler: echo-docker
- source: ./echo-docker
- description: "Dockerfile runtime function"
- environment:
- MY_FUNCTION: echo-docker
```
Explanation of the fields used above:
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4bdac278add..ee8480122c4 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -922,7 +922,15 @@ module API
end
class NamespaceBasic < Grape::Entity
- expose :id, :name, :path, :kind, :full_path, :parent_id
+ expose :id, :name, :path, :kind, :full_path, :parent_id, :avatar_url
+
+ expose :web_url do |namespace|
+ if namespace.user?
+ Gitlab::Routing.url_helpers.user_url(namespace.owner)
+ else
+ namespace.web_url
+ end
+ end
end
class Namespace < NamespaceBasic
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index c60d25b88cb..ea36c24eca2 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -15,12 +15,14 @@ module API
optional :info, type: Hash, desc: %q(Runner's metadata)
optional :active, type: Boolean, desc: 'Should Runner be active'
optional :locked, type: Boolean, desc: 'Should Runner be locked for current project'
+ optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
+ desc: 'The access_level of the runner'
optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs'
optional :tag_list, type: Array[String], desc: %q(List of Runner's tags)
optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job'
end
post '/' do
- attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :maximum_timeout])
+ attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :access_level, :maximum_timeout])
.merge(get_runner_details_from_request)
attributes =
diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
index 41135ae62bb..bb2b209e793 100644
--- a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
+++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
@@ -6,7 +6,9 @@ module Gitlab
module Prerequisite
class KubernetesNamespace < Base
def unmet?
- deployment_cluster.present? && kubernetes_namespace.new_record?
+ deployment_cluster.present? &&
+ !deployment_cluster.project_type? &&
+ kubernetes_namespace.new_record?
end
def complete!
diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb
index f6777dfa0c3..bce4fa14fb4 100644
--- a/lib/gitlab/git/rugged_impl/commit.rb
+++ b/lib/gitlab/git/rugged_impl/commit.rb
@@ -21,6 +21,17 @@ module Gitlab
nil
end
+ # This needs to return an array of Gitlab::Git:Commit objects
+ # instead of Rugged::Commit objects to ensure upstream models
+ # operate on a consistent interface. Unlike
+ # Gitlab::Git::Commit.find, Gitlab::Git::Commit.batch_by_oid
+ # doesn't attempt to decorate the result.
+ def rugged_batch_by_oid(repo, oids)
+ oids.map { |oid| rugged_find(repo, oid) }
+ .compact
+ .map { |commit| decorate(repo, commit) }
+ end
+
override :find_commit
def find_commit(repo, commit_id)
if Feature.enabled?(:rugged_find_commit)
@@ -29,6 +40,15 @@ module Gitlab
super
end
end
+
+ override :batch_by_oid
+ def batch_by_oid(repo, oids)
+ if Feature.enabled?(:rugged_list_commits_by_oid)
+ rugged_batch_by_oid(repo, oids)
+ else
+ super
+ end
+ end
end
extend ::Gitlab::Utils::Override
diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb
index c0a91f59ab9..e91b0ddcd31 100644
--- a/lib/gitlab/git/rugged_impl/repository.rb
+++ b/lib/gitlab/git/rugged_impl/repository.rb
@@ -12,7 +12,7 @@ module Gitlab
module Repository
extend ::Gitlab::Utils::Override
- FEATURE_FLAGS = %i(rugged_find_commit rugged_tree_entries rugged_tree_entry rugged_commit_is_ancestor rugged_commit_tree_entry).freeze
+ FEATURE_FLAGS = %i(rugged_find_commit rugged_tree_entries rugged_tree_entry rugged_commit_is_ancestor rugged_commit_tree_entry rugged_list_commits_by_oid).freeze
def alternate_object_directories
relative_object_directories.map { |d| File.join(path, d) }
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 35565b68388..c432317eb24 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -52,9 +52,9 @@ module Gitlab
end
def self.interceptors
- return [] unless Gitlab::Tracing.enabled?
+ return [] unless Labkit::Tracing.enabled?
- [Gitlab::Tracing::GRPCInterceptor.instance]
+ [Labkit::Tracing::GRPCInterceptor.instance]
end
private_class_method :interceptors
@@ -165,7 +165,10 @@ module Gitlab
current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s),
duration)
- add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc)
+ if peek_enabled?
+ add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc,
+ backtrace: Gitlab::Profiler.clean_backtrace(caller))
+ end
end
def self.query_time
@@ -215,7 +218,7 @@ module Gitlab
feature = feature_stack && feature_stack[0]
metadata['call_site'] = feature.to_s if feature
metadata['gitaly-servers'] = address_metadata(remote_storage) if remote_storage
- metadata['x-gitlab-correlation-id'] = Gitlab::CorrelationId.current_id if Gitlab::CorrelationId.current_id
+ metadata['x-gitlab-correlation-id'] = Labkit::Correlation::CorrelationId.current_id if Labkit::Correlation::CorrelationId.current_id
metadata.merge!(server_feature_flags)
@@ -350,15 +353,17 @@ module Gitlab
Gitlab::SafeRequestStore["gitaly_call_permitted"] = 0
end
- def self.add_call_details(details)
- return unless Gitlab::SafeRequestStore[:peek_enabled]
+ def self.peek_enabled?
+ Gitlab::SafeRequestStore[:peek_enabled]
+ end
+ def self.add_call_details(details)
Gitlab::SafeRequestStore['gitaly_call_details'] ||= []
Gitlab::SafeRequestStore['gitaly_call_details'] << details
end
def self.list_call_details
- return [] unless Gitlab::SafeRequestStore[:peek_enabled]
+ return [] unless peek_enabled?
Gitlab::SafeRequestStore['gitaly_call_details'] || []
end
diff --git a/lib/gitlab/grape_logging/loggers/correlation_id_logger.rb b/lib/gitlab/grape_logging/loggers/correlation_id_logger.rb
index fa4c5d86d44..bbe4b24c7d4 100644
--- a/lib/gitlab/grape_logging/loggers/correlation_id_logger.rb
+++ b/lib/gitlab/grape_logging/loggers/correlation_id_logger.rb
@@ -6,7 +6,7 @@ module Gitlab
module Loggers
class CorrelationIdLogger < ::GrapeLogging::Loggers::Base
def parameters(_, _)
- { Gitlab::CorrelationId::LOG_KEY => Gitlab::CorrelationId.current_id }
+ { Labkit::Correlation::CorrelationId::LOG_KEY => Labkit::Correlation::CorrelationId.current_id }
end
end
end
diff --git a/lib/gitlab/json_logger.rb b/lib/gitlab/json_logger.rb
index a5a5759cc89..ab34fb03158 100644
--- a/lib/gitlab/json_logger.rb
+++ b/lib/gitlab/json_logger.rb
@@ -10,7 +10,7 @@ module Gitlab
data = {}
data[:severity] = severity
data[:time] = timestamp.utc.iso8601(3)
- data[Gitlab::CorrelationId::LOG_KEY] = Gitlab::CorrelationId.current_id
+ data[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id
case message
when String
diff --git a/lib/gitlab/middleware/correlation_id.rb b/lib/gitlab/middleware/correlation_id.rb
index 80dddc41c12..fffd5da827f 100644
--- a/lib/gitlab/middleware/correlation_id.rb
+++ b/lib/gitlab/middleware/correlation_id.rb
@@ -12,7 +12,7 @@ module Gitlab
end
def call(env)
- ::Gitlab::CorrelationId.use_id(correlation_id(env)) do
+ ::Labkit::Correlation::CorrelationId.use_id(correlation_id(env)) do
@app.call(env)
end
end
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index 93a9fcf1591..28ed587f5c7 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -16,7 +16,11 @@ module Gitlab
ee/lib/gitlab/middleware/
lib/gitlab/performance_bar/
lib/gitlab/request_profiler/
+ lib/gitlab/query_limiting/
+ lib/gitlab/tracing/
lib/gitlab/profiler.rb
+ lib/gitlab/correlation_id.rb
+ lib/gitlab/webpack/dev_server_middleware.rb
].freeze
# Takes a URL to profile (can be a fully-qualified URL, or an absolute path)
diff --git a/lib/gitlab/sentry.rb b/lib/gitlab/sentry.rb
index 956c16117f5..356e6445e0e 100644
--- a/lib/gitlab/sentry.rb
+++ b/lib/gitlab/sentry.rb
@@ -45,7 +45,7 @@ module Gitlab
context # Make sure we've set everything we know in the context
tags = {
- Gitlab::CorrelationId::LOG_KEY.to_sym => Gitlab::CorrelationId.current_id
+ Labkit::Correlation::CorrelationId::LOG_KEY.to_sym => Labkit::Correlation::CorrelationId.current_id
}
Raven.capture_exception(exception, tags: tags, extra: extra)
diff --git a/lib/gitlab/sidekiq_middleware/correlation_injector.rb b/lib/gitlab/sidekiq_middleware/correlation_injector.rb
index b807b3a03ed..1539fd706ab 100644
--- a/lib/gitlab/sidekiq_middleware/correlation_injector.rb
+++ b/lib/gitlab/sidekiq_middleware/correlation_injector.rb
@@ -4,8 +4,8 @@ module Gitlab
module SidekiqMiddleware
class CorrelationInjector
def call(worker_class, job, queue, redis_pool)
- job[Gitlab::CorrelationId::LOG_KEY] ||=
- Gitlab::CorrelationId.current_or_new_id
+ job[Labkit::Correlation::CorrelationId::LOG_KEY] ||=
+ Labkit::Correlation::CorrelationId.current_or_new_id
yield
end
diff --git a/lib/gitlab/sidekiq_middleware/correlation_logger.rb b/lib/gitlab/sidekiq_middleware/correlation_logger.rb
index cb8ff4a6284..cffc4483573 100644
--- a/lib/gitlab/sidekiq_middleware/correlation_logger.rb
+++ b/lib/gitlab/sidekiq_middleware/correlation_logger.rb
@@ -4,9 +4,9 @@ module Gitlab
module SidekiqMiddleware
class CorrelationLogger
def call(worker, job, queue)
- correlation_id = job[Gitlab::CorrelationId::LOG_KEY]
+ correlation_id = job[Labkit::Correlation::CorrelationId::LOG_KEY]
- Gitlab::CorrelationId.use_id(correlation_id) do
+ Labkit::Correlation::CorrelationId.use_id(correlation_id) do
yield
end
end
diff --git a/lib/gitlab/tracing/common.rb b/lib/gitlab/tracing/common.rb
deleted file mode 100644
index 3a08ede8138..00000000000
--- a/lib/gitlab/tracing/common.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-require 'opentracing'
-
-module Gitlab
- module Tracing
- module Common
- def tracer
- OpenTracing.global_tracer
- end
-
- # Convience method for running a block with a span
- def in_tracing_span(operation_name:, tags:, child_of: nil)
- scope = tracer.start_active_span(
- operation_name,
- child_of: child_of,
- tags: tags
- )
- span = scope.span
-
- # Add correlation details to the span if we have them
- correlation_id = Gitlab::CorrelationId.current_id
- if correlation_id
- span.set_tag('correlation_id', correlation_id)
- end
-
- begin
- yield span
- rescue => e
- log_exception_on_span(span, e)
- raise e
- ensure
- scope.close
- end
- end
-
- def postnotify_span(operation_name, start_time, end_time, tags: nil, child_of: nil, exception: nil)
- span = OpenTracing.start_span(operation_name, start_time: start_time, tags: tags, child_of: child_of)
-
- log_exception_on_span(span, exception) if exception
-
- span.finish(end_time: end_time)
- end
-
- def log_exception_on_span(span, exception)
- span.set_tag('error', true)
- span.log_kv(kv_tags_for_exception(exception))
- end
-
- def kv_tags_for_exception(exception)
- case exception
- when Exception
- {
- 'event': 'error',
- 'error.kind': exception.class.to_s,
- 'message': Gitlab::UrlSanitizer.sanitize(exception.message),
- 'stack': exception.backtrace&.join("\n")
- }
- else
- {
- 'event': 'error',
- 'error.kind': exception.class.to_s,
- 'error.object': Gitlab::UrlSanitizer.sanitize(exception.to_s)
- }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/factory.rb b/lib/gitlab/tracing/factory.rb
deleted file mode 100644
index fc714164353..00000000000
--- a/lib/gitlab/tracing/factory.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-require "cgi"
-
-module Gitlab
- module Tracing
- class Factory
- OPENTRACING_SCHEME = "opentracing"
-
- def self.create_tracer(service_name, connection_string)
- return unless connection_string.present?
-
- begin
- opentracing_details = parse_connection_string(connection_string)
- driver_name = opentracing_details[:driver_name]
-
- case driver_name
- when "jaeger"
- JaegerFactory.create_tracer(service_name, opentracing_details[:options])
- else
- raise "Unknown driver: #{driver_name}"
- end
- rescue => e
- # Can't create the tracer? Warn and continue sans tracer
- warn "Unable to instantiate tracer: #{e}"
- nil
- end
- end
-
- def self.parse_connection_string(connection_string)
- parsed = URI.parse(connection_string)
-
- unless valid_uri?(parsed)
- raise "Invalid tracing connection string"
- end
-
- {
- driver_name: parsed.host,
- options: parse_query(parsed.query)
- }
- end
- private_class_method :parse_connection_string
-
- def self.parse_query(query)
- return {} unless query
-
- CGI.parse(query).symbolize_keys.transform_values(&:first)
- end
- private_class_method :parse_query
-
- def self.valid_uri?(uri)
- return false unless uri
-
- uri.scheme == OPENTRACING_SCHEME &&
- uri.host.to_s =~ /^[a-z0-9_]+$/ &&
- uri.path.empty?
- end
- private_class_method :valid_uri?
- end
- end
-end
diff --git a/lib/gitlab/tracing/grpc_interceptor.rb b/lib/gitlab/tracing/grpc_interceptor.rb
deleted file mode 100644
index 6c2aab73125..00000000000
--- a/lib/gitlab/tracing/grpc_interceptor.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-require 'opentracing'
-require 'grpc'
-
-module Gitlab
- module Tracing
- class GRPCInterceptor < GRPC::ClientInterceptor
- include Common
- include Singleton
-
- def request_response(request:, call:, method:, metadata:)
- wrap_with_tracing(method, 'unary', metadata) do
- yield
- end
- end
-
- def client_streamer(requests:, call:, method:, metadata:)
- wrap_with_tracing(method, 'client_stream', metadata) do
- yield
- end
- end
-
- def server_streamer(request:, call:, method:, metadata:)
- wrap_with_tracing(method, 'server_stream', metadata) do
- yield
- end
- end
-
- def bidi_streamer(requests:, call:, method:, metadata:)
- wrap_with_tracing(method, 'bidi_stream', metadata) do
- yield
- end
- end
-
- private
-
- def wrap_with_tracing(method, grpc_type, metadata)
- tags = {
- 'component' => 'grpc',
- 'span.kind' => 'client',
- 'grpc.method' => method,
- 'grpc.type' => grpc_type
- }
-
- in_tracing_span(operation_name: "grpc:#{method}", tags: tags) do |span|
- OpenTracing.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, metadata)
-
- yield
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/jaeger_factory.rb b/lib/gitlab/tracing/jaeger_factory.rb
deleted file mode 100644
index 93520d5667b..00000000000
--- a/lib/gitlab/tracing/jaeger_factory.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-# frozen_string_literal: true
-
-require 'jaeger/client'
-
-module Gitlab
- module Tracing
- class JaegerFactory
- # When the probabilistic sampler is used, by default 0.1% of requests will be traced
- DEFAULT_PROBABILISTIC_RATE = 0.001
-
- # The default port for the Jaeger agent UDP listener
- DEFAULT_UDP_PORT = 6831
-
- # Reduce this from default of 10 seconds as the Ruby jaeger
- # client doesn't have overflow control, leading to very large
- # messages which fail to send over UDP (max packet = 64k)
- # Flush more often, with smaller packets
- FLUSH_INTERVAL = 5
-
- def self.create_tracer(service_name, options)
- kwargs = {
- service_name: service_name,
- sampler: get_sampler(options[:sampler], options[:sampler_param]),
- reporter: get_reporter(service_name, options[:http_endpoint], options[:udp_endpoint])
- }.compact
-
- extra_params = options.except(:sampler, :sampler_param, :http_endpoint, :udp_endpoint, :strict_parsing, :debug) # rubocop: disable CodeReuse/ActiveRecord
- if extra_params.present?
- message = "jaeger tracer: invalid option: #{extra_params.keys.join(", ")}"
-
- if options[:strict_parsing]
- raise message
- else
- warn message
- end
- end
-
- Jaeger::Client.build(kwargs)
- end
-
- def self.get_sampler(sampler_type, sampler_param)
- case sampler_type
- when "probabilistic"
- sampler_rate = sampler_param ? sampler_param.to_f : DEFAULT_PROBABILISTIC_RATE
- Jaeger::Samplers::Probabilistic.new(rate: sampler_rate)
- when "const"
- const_value = sampler_param == "1"
- Jaeger::Samplers::Const.new(const_value)
- else
- nil
- end
- end
- private_class_method :get_sampler
-
- def self.get_reporter(service_name, http_endpoint, udp_endpoint)
- encoder = Jaeger::Encoders::ThriftEncoder.new(service_name: service_name)
-
- if http_endpoint.present?
- sender = get_http_sender(encoder, http_endpoint)
- elsif udp_endpoint.present?
- sender = get_udp_sender(encoder, udp_endpoint)
- else
- return
- end
-
- Jaeger::Reporters::RemoteReporter.new(
- sender: sender,
- flush_interval: FLUSH_INTERVAL
- )
- end
- private_class_method :get_reporter
-
- def self.get_http_sender(encoder, address)
- Jaeger::HttpSender.new(
- url: address,
- encoder: encoder,
- logger: Logger.new(STDOUT)
- )
- end
- private_class_method :get_http_sender
-
- def self.get_udp_sender(encoder, address)
- pair = address.split(":", 2)
- host = pair[0]
- port = pair[1] ? pair[1].to_i : DEFAULT_UDP_PORT
-
- Jaeger::UdpSender.new(
- host: host,
- port: port,
- encoder: encoder,
- logger: Logger.new(STDOUT)
- )
- end
- private_class_method :get_udp_sender
- end
- end
-end
diff --git a/lib/gitlab/tracing/rack_middleware.rb b/lib/gitlab/tracing/rack_middleware.rb
deleted file mode 100644
index e6a31293f7b..00000000000
--- a/lib/gitlab/tracing/rack_middleware.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require 'opentracing'
-
-module Gitlab
- module Tracing
- class RackMiddleware
- include Common
-
- REQUEST_METHOD = 'REQUEST_METHOD'
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- method = env[REQUEST_METHOD]
-
- context = tracer.extract(OpenTracing::FORMAT_RACK, env)
- tags = {
- 'component' => 'rack',
- 'span.kind' => 'server',
- 'http.method' => method,
- 'http.url' => self.class.build_sanitized_url_from_env(env)
- }
-
- in_tracing_span(operation_name: "http:#{method}", child_of: context, tags: tags) do |span|
- @app.call(env).tap do |status_code, _headers, _body|
- span.set_tag('http.status_code', status_code)
- end
- end
- end
-
- # Generate a sanitized (safe) request URL from the rack environment
- def self.build_sanitized_url_from_env(env)
- request = ActionDispatch::Request.new(env)
-
- original_url = request.original_url
- uri = URI.parse(original_url)
- uri.query = request.filtered_parameters.to_query if uri.query.present?
-
- uri.to_s
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/rails/action_view_subscriber.rb b/lib/gitlab/tracing/rails/action_view_subscriber.rb
deleted file mode 100644
index 88816e1fb32..00000000000
--- a/lib/gitlab/tracing/rails/action_view_subscriber.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Tracing
- module Rails
- class ActionViewSubscriber
- include RailsCommon
-
- COMPONENT_TAG = 'ActionView'
- RENDER_TEMPLATE_NOTIFICATION_TOPIC = 'render_template.action_view'
- RENDER_COLLECTION_NOTIFICATION_TOPIC = 'render_collection.action_view'
- RENDER_PARTIAL_NOTIFICATION_TOPIC = 'render_partial.action_view'
-
- # Instruments Rails ActionView events for opentracing.
- # Returns a lambda, which, when called will unsubscribe from the notifications
- def self.instrument
- subscriber = new
-
- subscriptions = [
- ActiveSupport::Notifications.subscribe(RENDER_TEMPLATE_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
- subscriber.notify_render_template(start, finish, payload)
- end,
- ActiveSupport::Notifications.subscribe(RENDER_COLLECTION_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
- subscriber.notify_render_collection(start, finish, payload)
- end,
- ActiveSupport::Notifications.subscribe(RENDER_PARTIAL_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
- subscriber.notify_render_partial(start, finish, payload)
- end
- ]
-
- create_unsubscriber subscriptions
- end
-
- # For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
- def notify_render_template(start, finish, payload)
- generate_span_for_notification("render_template", start, finish, payload, tags_for_render_template(payload))
- end
-
- def notify_render_collection(start, finish, payload)
- generate_span_for_notification("render_collection", start, finish, payload, tags_for_render_collection(payload))
- end
-
- def notify_render_partial(start, finish, payload)
- generate_span_for_notification("render_partial", start, finish, payload, tags_for_render_partial(payload))
- end
-
- private
-
- def tags_for_render_template(payload)
- {
- 'component' => COMPONENT_TAG,
- 'template.id' => payload[:identifier],
- 'template.layout' => payload[:layout]
- }
- end
-
- def tags_for_render_collection(payload)
- {
- 'component' => COMPONENT_TAG,
- 'template.id' => payload[:identifier],
- 'template.count' => payload[:count] || 0,
- 'template.cache.hits' => payload[:cache_hits] || 0
- }
- end
-
- def tags_for_render_partial(payload)
- {
- 'component' => COMPONENT_TAG,
- 'template.id' => payload[:identifier]
- }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/rails/active_record_subscriber.rb b/lib/gitlab/tracing/rails/active_record_subscriber.rb
deleted file mode 100644
index 32f5658e57e..00000000000
--- a/lib/gitlab/tracing/rails/active_record_subscriber.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Tracing
- module Rails
- class ActiveRecordSubscriber
- include RailsCommon
-
- ACTIVE_RECORD_NOTIFICATION_TOPIC = 'sql.active_record'
- OPERATION_NAME_PREFIX = 'active_record:'
- DEFAULT_OPERATION_NAME = 'sqlquery'
-
- # Instruments Rails ActiveRecord events for opentracing.
- # Returns a lambda, which, when called will unsubscribe from the notifications
- def self.instrument
- subscriber = new
-
- subscription = ActiveSupport::Notifications.subscribe(ACTIVE_RECORD_NOTIFICATION_TOPIC) do |_, start, finish, _, payload|
- subscriber.notify(start, finish, payload)
- end
-
- create_unsubscriber [subscription]
- end
-
- # For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
- def notify(start, finish, payload)
- generate_span_for_notification(notification_name(payload), start, finish, payload, tags_for_notification(payload))
- end
-
- private
-
- def notification_name(payload)
- OPERATION_NAME_PREFIX + (payload[:name].presence || DEFAULT_OPERATION_NAME)
- end
-
- def tags_for_notification(payload)
- {
- 'component' => 'ActiveRecord',
- 'span.kind' => 'client',
- 'db.type' => 'sql',
- 'db.connection_id' => payload[:connection_id],
- 'db.cached' => payload[:cached] || false,
- 'db.statement' => payload[:sql]
- }
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/rails/rails_common.rb b/lib/gitlab/tracing/rails/rails_common.rb
deleted file mode 100644
index 88e914f62f8..00000000000
--- a/lib/gitlab/tracing/rails/rails_common.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Tracing
- module Rails
- module RailsCommon
- extend ActiveSupport::Concern
- include Gitlab::Tracing::Common
-
- class_methods do
- def create_unsubscriber(subscriptions)
- -> { subscriptions.each { |subscriber| ActiveSupport::Notifications.unsubscribe(subscriber) } }
- end
- end
-
- def generate_span_for_notification(operation_name, start, finish, payload, tags)
- exception = payload[:exception]
-
- postnotify_span(operation_name, start, finish, tags: tags, exception: exception)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/sidekiq/client_middleware.rb b/lib/gitlab/tracing/sidekiq/client_middleware.rb
deleted file mode 100644
index 2b71c1ea21e..00000000000
--- a/lib/gitlab/tracing/sidekiq/client_middleware.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-require 'opentracing'
-
-module Gitlab
- module Tracing
- module Sidekiq
- class ClientMiddleware
- include SidekiqCommon
-
- SPAN_KIND = 'client'
-
- def call(worker_class, job, queue, redis_pool)
- in_tracing_span(
- operation_name: "sidekiq:#{job['class']}",
- tags: tags_from_job(job, SPAN_KIND)) do |span|
- # Inject the details directly into the job
- tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, job)
-
- yield
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/sidekiq/server_middleware.rb b/lib/gitlab/tracing/sidekiq/server_middleware.rb
deleted file mode 100644
index 5b43c4310e6..00000000000
--- a/lib/gitlab/tracing/sidekiq/server_middleware.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-require 'opentracing'
-
-module Gitlab
- module Tracing
- module Sidekiq
- class ServerMiddleware
- include SidekiqCommon
-
- SPAN_KIND = 'server'
-
- def call(worker, job, queue)
- context = tracer.extract(OpenTracing::FORMAT_TEXT_MAP, job)
-
- in_tracing_span(
- operation_name: "sidekiq:#{job['class']}",
- child_of: context,
- tags: tags_from_job(job, SPAN_KIND)) do |span|
- yield
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/tracing/sidekiq/sidekiq_common.rb b/lib/gitlab/tracing/sidekiq/sidekiq_common.rb
deleted file mode 100644
index a911a29d773..00000000000
--- a/lib/gitlab/tracing/sidekiq/sidekiq_common.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Tracing
- module Sidekiq
- module SidekiqCommon
- include Gitlab::Tracing::Common
-
- def tags_from_job(job, kind)
- {
- 'component' => 'sidekiq',
- 'span.kind' => kind,
- 'sidekiq.queue' => job['queue'],
- 'sidekiq.jid' => job['jid'],
- 'sidekiq.retry' => job['retry'].to_s,
- 'sidekiq.args' => job['args']&.join(", ")
- }
- end
- end
- end
- end
-end
diff --git a/lib/peek/views/tracing.rb b/lib/peek/views/tracing.rb
index 0de32a8fdda..94726a498ea 100644
--- a/lib/peek/views/tracing.rb
+++ b/lib/peek/views/tracing.rb
@@ -4,9 +4,9 @@ module Peek
module Views
class Tracing < View
def results
- {
- tracing_url: Gitlab::Tracing.tracing_url
- }
+ tracing_url = Labkit::Tracing.tracing_url(Gitlab.process_name)
+
+ { tracing_url: tracing_url }
end
end
end
diff --git a/lib/system_check/app/ruby_version_check.rb b/lib/system_check/app/ruby_version_check.rb
index 60e07718338..53da62df176 100644
--- a/lib/system_check/app/ruby_version_check.rb
+++ b/lib/system_check/app/ruby_version_check.rb
@@ -7,7 +7,7 @@ module SystemCheck
set_check_pass -> { "yes (#{self.current_version})" }
def self.required_version
- @required_version ||= Gitlab::VersionInfo.new(2, 3, 5)
+ @required_version ||= Gitlab::VersionInfo.new(2, 5, 3)
end
def self.current_version
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fc412a936e9..54c40e48084 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -375,12 +375,6 @@ msgstr ""
msgid "A member of GitLab's abuse team will review your report as soon as possible."
msgstr ""
-msgid "A merge commit is created for every merge, and merging is allowed as long as there are no conflicts."
-msgstr ""
-
-msgid "A merge commit is created for every merge, but merging is only allowed if fast-forward merge is possible. This way you could make sure that if this merge request would build, after merging to target branch it would also build."
-msgstr ""
-
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
@@ -1173,9 +1167,6 @@ msgstr ""
msgid "Automatically marked as default internal user"
msgstr ""
-msgid "Automatically resolve merge request diff discussions when they become outdated"
-msgstr ""
-
msgid "Automatically resolved"
msgstr ""
@@ -1722,7 +1713,7 @@ msgstr ""
msgid "Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions."
msgstr ""
-msgid "Choose your merge method, set up a default merge request description template."
+msgid "Choose your merge method, options, checks, and set up a default merge request description template."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -4003,9 +3994,6 @@ msgstr ""
msgid "Failure"
msgstr ""
-msgid "Fast-forward merge"
-msgstr ""
-
msgid "Fast-forward merge without a merge commit"
msgstr ""
@@ -5260,6 +5248,9 @@ msgstr ""
msgid "Lead"
msgstr ""
+msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
+msgstr ""
+
msgid "Learn more"
msgstr ""
@@ -5505,15 +5496,9 @@ msgstr ""
msgid "Merge Requests"
msgstr ""
-msgid "Merge commit"
-msgstr ""
-
msgid "Merge commit message"
msgstr ""
-msgid "Merge commit with semi-linear history"
-msgstr ""
-
msgid "Merge events"
msgstr ""
@@ -5523,9 +5508,6 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
-msgid "Merge method"
-msgstr ""
-
msgid "Merge request"
msgstr ""
@@ -5966,9 +5948,6 @@ msgstr ""
msgid "No details available"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No errors to display."
msgstr ""
@@ -5993,9 +5972,6 @@ msgstr ""
msgid "No license. All rights reserved"
msgstr ""
-msgid "No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. When fast-forward merge is not possible, the user is given the option to rebase."
-msgstr ""
-
msgid "No merge requests found"
msgstr ""
@@ -6190,12 +6166,6 @@ msgstr ""
msgid "Only admins"
msgstr ""
-msgid "Only allow merge requests to be merged if all discussions are resolved"
-msgstr ""
-
-msgid "Only allow merge requests to be merged if the pipeline succeeds"
-msgstr ""
-
msgid "Only mirror protected branches"
msgstr ""
@@ -6460,9 +6430,6 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
-msgid "Pipelines need to be configured to enable this feature."
-msgstr ""
-
msgid "Pipelines settings for '%{project_name}' were successfully updated."
msgstr ""
@@ -7096,15 +7063,69 @@ msgstr ""
msgid "ProjectPage|Project ID: %{project_id}"
msgstr ""
+msgid "ProjectSettings|Additional merge request capabilities that influence how and when merges will be performed"
+msgstr ""
+
+msgid "ProjectSettings|All discussions must be resolved"
+msgstr ""
+
+msgid "ProjectSettings|Automatically resolve merge request diff discussions when they become outdated"
+msgstr ""
+
msgid "ProjectSettings|Badges"
msgstr ""
msgid "ProjectSettings|Customize your project badges."
msgstr ""
+msgid "ProjectSettings|Every merge creates a merge commit"
+msgstr ""
+
+msgid "ProjectSettings|Fast-forward merge"
+msgstr ""
+
+msgid "ProjectSettings|Fast-forward merges only"
+msgstr ""
+
msgid "ProjectSettings|Learn more about badges."
msgstr ""
+msgid "ProjectSettings|Merge checks"
+msgstr ""
+
+msgid "ProjectSettings|Merge commit"
+msgstr ""
+
+msgid "ProjectSettings|Merge commit with semi-linear history"
+msgstr ""
+
+msgid "ProjectSettings|Merge method"
+msgstr ""
+
+msgid "ProjectSettings|Merge options"
+msgstr ""
+
+msgid "ProjectSettings|No merge commits are created"
+msgstr ""
+
+msgid "ProjectSettings|Pipelines must succeed"
+msgstr ""
+
+msgid "ProjectSettings|Pipelines need to be configured to enable this feature."
+msgstr ""
+
+msgid "ProjectSettings|Show link to create/view merge request when pushing from the command line"
+msgstr ""
+
+msgid "ProjectSettings|These checks must pass before merge requests can be merged"
+msgstr ""
+
+msgid "ProjectSettings|This will dictate the commit history when you merge a merge request"
+msgstr ""
+
+msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
+msgstr ""
+
msgid "Projects"
msgstr ""
@@ -8147,9 +8168,6 @@ msgstr ""
msgid "Show latest version"
msgstr ""
-msgid "Show link to create/view merge request when pushing from the command line"
-msgstr ""
-
msgid "Show parent pages"
msgstr ""
@@ -9565,6 +9583,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle backtrace"
+msgstr ""
+
msgid "Toggle comments for this file"
msgstr ""
@@ -10165,9 +10186,6 @@ msgstr ""
msgid "When enabled, users cannot use GitLab until the terms have been accepted."
msgstr ""
-msgid "When fast-forward merge is not possible, the user is given the option to rebase."
-msgstr ""
-
msgid "When leaving the URL blank, classification labels can still be specified without disabling cross project features or performing external authorization checks."
msgstr ""
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index f5add6bc9b5..07e191f1c9b 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -15,7 +15,7 @@ module QA
element :reply_comment_button
end
- base.view 'app/assets/javascripts/notes/components/noteable_discussion.vue' do
+ base.view 'app/assets/javascripts/notes/components/discussion_actions.vue' do
element :discussion_reply
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index a5ecb475ce3..7296a4b4526 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -462,7 +462,7 @@ describe ApplicationController do
end
it 'does log correlation id' do
- Gitlab::CorrelationId.use_id('new-id') do
+ Labkit::Correlation::CorrelationId.use_id('new-id') do
get :index
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index e6887f0c899..bc0ec58bd24 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -775,10 +775,10 @@ describe 'Issues' do
wait_for_requests
- expect(page).to have_no_content 'No due date'
+ expect(page).to have_no_content 'None'
click_link 'remove due date'
- expect(page).to have_content 'No due date'
+ expect(page).to have_content 'None'
end
end
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index ec3930c26db..2de22582b2c 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -171,7 +171,7 @@ describe 'Projects > Files > User edits files', :js do
wait_for_requests
end
- it 'links to the forked project for editing' do
+ it 'links to the forked project for editing', :quarantine do
click_link('.gitignore')
find('.js-edit-blob').click
diff --git a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
index b1c2bab08c0..28d52f25f56 100644
--- a/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
require 'spec_helper'
describe 'Projects > Settings > User manages merge request settings' do
@@ -30,16 +31,16 @@ describe 'Projects > Settings > User manages merge request settings' do
context 'when Merge Request and Pipelines are initially enabled', :js do
context 'when Pipelines are initially enabled' do
it 'shows the Merge Requests settings' do
- expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
- expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).not_to have_content 'Pipelines must succeed'
+ expect(page).not_to have_content 'All discussions must be resolved'
end
end
@@ -50,16 +51,16 @@ describe 'Projects > Settings > User manages merge request settings' do
end
it 'shows the Merge Requests settings that do not depend on Builds feature' do
- expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).not_to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .project-feature-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
- expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All discussions must be resolved'
end
end
end
@@ -71,16 +72,16 @@ describe 'Projects > Settings > User manages merge request settings' do
end
it 'does not show the Merge Requests settings' do
- expect(page).not_to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).not_to have_content 'Pipelines must succeed'
+ expect(page).not_to have_content 'All discussions must be resolved'
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .project-feature-toggle').click
find('input[value="Save changes"]').send_keys(:return)
end
- expect(page).to have_content('Only allow merge requests to be merged if the pipeline succeeds')
- expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved')
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All discussions must be resolved'
end
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index bcbba6f14da..aac095bfa6b 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -141,7 +141,7 @@ describe "User creates wiki page" do
end
end
- it_behaves_like 'wiki file attachments'
+ it_behaves_like 'wiki file attachments', :quarantine
end
context "in a group namespace", :js do
@@ -151,7 +151,7 @@ describe "User creates wiki page" do
expect(page).to have_field("wiki[message]", with: "Create home")
end
- it "creates a page from the home page" do
+ it "creates a page from the home page", :quarantine do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
index abd0d6b5185..bcde115b1a6 100644
--- a/spec/finders/autocomplete/users_finder_spec.rb
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -26,9 +26,17 @@ describe Autocomplete::UsersFinder do
it { is_expected.to match_array([project.owner]) }
context 'when author_id passed' do
- let(:params) { { author_id: user2.id } }
+ context 'and author is active' do
+ let(:params) { { author_id: user1.id } }
- it { is_expected.to match_array([project.owner, user2]) }
+ it { is_expected.to match_array([project.owner, user1]) }
+ end
+
+ context 'and author is blocked' do
+ let(:params) { { author_id: user2.id } }
+
+ it { is_expected.to match_array([project.owner]) }
+ end
end
end
@@ -104,9 +112,9 @@ describe Autocomplete::UsersFinder do
end
context 'when filtered by author_id' do
- let(:params) { { author_id: user2.id } }
+ let(:params) { { author_id: user1.id } }
- it { is_expected.to match_array([user2, user1, external_user, omniauth_user, current_user]) }
+ it { is_expected.to match_array([user1, external_user, omniauth_user, current_user]) }
end
end
end
diff --git a/spec/frontend/clusters/components/application_row_spec.js b/spec/frontend/clusters/components/application_row_spec.js
index b28d0075d06..038d2be9e98 100644
--- a/spec/frontend/clusters/components/application_row_spec.js
+++ b/spec/frontend/clusters/components/application_row_spec.js
@@ -114,10 +114,12 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has disabled "Installed" when APPLICATION_STATUS.INSTALLED', () => {
+ it('has disabled "Installed" when application is installed and not uninstallable', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.INSTALLED,
+ installed: true,
+ uninstallable: false,
});
expect(vm.installButtonLabel).toEqual('Installed');
@@ -125,15 +127,16 @@ describe('Application Row', () => {
expect(vm.installButtonDisabled).toEqual(true);
});
- it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => {
+ it('hides when application is installed and uninstallable', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
- status: APPLICATION_STATUS.UPDATING,
+ status: APPLICATION_STATUS.INSTALLED,
+ installed: true,
+ uninstallable: true,
});
+ const installBtn = vm.$el.querySelector('.js-cluster-application-install-button');
- expect(vm.installButtonLabel).toEqual('Installed');
- expect(vm.installButtonLoading).toEqual(false);
- expect(vm.installButtonDisabled).toEqual(true);
+ expect(installBtn).toBe(null);
});
it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => {
@@ -208,6 +211,19 @@ describe('Application Row', () => {
});
});
+ describe('Uninstall button', () => {
+ it('displays button when app is installed and uninstallable', () => {
+ vm = mountComponent(ApplicationRow, {
+ ...DEFAULT_APPLICATION_STATE,
+ installed: true,
+ uninstallable: true,
+ });
+ const uninstallButton = vm.$el.querySelector('.js-cluster-application-uninstall-button');
+
+ expect(uninstallButton).toBeTruthy();
+ });
+ });
+
describe('Upgrade button', () => {
it('has indeterminate state on page load', () => {
vm = mountComponent(ApplicationRow, {
diff --git a/spec/frontend/clusters/stores/clusters_store_spec.js b/spec/frontend/clusters/stores/clusters_store_spec.js
index 161722ec571..c0e8b737ea2 100644
--- a/spec/frontend/clusters/stores/clusters_store_spec.js
+++ b/spec/frontend/clusters/stores/clusters_store_spec.js
@@ -1,5 +1,5 @@
import ClustersStore from '~/clusters/stores/clusters_store';
-import { APPLICATION_STATUS } from '~/clusters/constants';
+import { APPLICATION_INSTALLED_STATUSES, APPLICATION_STATUS, RUNNER } from '~/clusters/constants';
import { CLUSTERS_MOCK_DATA } from '../services/mock_data';
describe('Clusters Store', () => {
@@ -70,6 +70,7 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[0].status_reason,
requestStatus: null,
requestReason: null,
+ installed: false,
},
ingress: {
title: 'Ingress',
@@ -79,6 +80,7 @@ describe('Clusters Store', () => {
requestReason: null,
externalIp: null,
externalHostname: null,
+ installed: false,
},
runner: {
title: 'GitLab Runner',
@@ -89,6 +91,7 @@ describe('Clusters Store', () => {
version: mockResponseData.applications[2].version,
upgradeAvailable: mockResponseData.applications[2].update_available,
chartRepo: 'https://gitlab.com/charts/gitlab-runner',
+ installed: false,
},
prometheus: {
title: 'Prometheus',
@@ -96,6 +99,7 @@ describe('Clusters Store', () => {
statusReason: mockResponseData.applications[3].status_reason,
requestStatus: null,
requestReason: null,
+ installed: false,
},
jupyter: {
title: 'JupyterHub',
@@ -104,6 +108,7 @@ describe('Clusters Store', () => {
requestStatus: null,
requestReason: null,
hostname: '',
+ installed: false,
},
knative: {
title: 'Knative',
@@ -115,6 +120,7 @@ describe('Clusters Store', () => {
isEditingHostName: false,
externalIp: null,
externalHostname: null,
+ installed: false,
},
cert_manager: {
title: 'Cert-Manager',
@@ -123,11 +129,26 @@ describe('Clusters Store', () => {
requestStatus: null,
requestReason: null,
email: mockResponseData.applications[6].email,
+ installed: false,
},
},
});
});
+ describe.each(APPLICATION_INSTALLED_STATUSES)('given the current app status is %s', () => {
+ it('marks application as installed', () => {
+ const mockResponseData =
+ CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
+ const runnerAppIndex = 2;
+
+ mockResponseData.applications[runnerAppIndex].status = APPLICATION_STATUS.INSTALLED;
+
+ store.updateStateFromServer(mockResponseData);
+
+ expect(store.state.applications[RUNNER].installed).toBe(true);
+ });
+ });
+
it('sets default hostname for jupyter when ingress has a ip address', () => {
const mockResponseData =
CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index ed12af925f1..841aff0d7ff 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -94,7 +94,7 @@ describe('GfmAutoComplete', () => {
});
it('should quote if value contains any non-alphanumeric characters', () => {
- expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"');
+ expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label-20"');
expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
});
@@ -102,12 +102,21 @@ describe('GfmAutoComplete', () => {
expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
});
- it('should escape Markdown emphasis characters, except in the first character', () => {
- expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group');
- expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug');
+ it('escapes Markdown strikethroughs when needed', () => {
+ expect(beforeInsert(atwhoInstance, '~a~bug')).toEqual('~"a~bug"');
+ expect(beforeInsert(atwhoInstance, '~a~~bug~~')).toEqual('~"a\\~~bug\\~~"');
+ });
+
+ it('escapes Markdown emphasis when needed', () => {
+ expect(beforeInsert(atwhoInstance, '~a_bug_')).toEqual('~a_bug\\_');
+ expect(beforeInsert(atwhoInstance, '~a _bug_')).toEqual('~"a \\_bug\\_"');
+ expect(beforeInsert(atwhoInstance, '~a*bug*')).toEqual('~"a\\*bug\\*"');
+ expect(beforeInsert(atwhoInstance, '~a *bug*')).toEqual('~"a \\*bug\\*"');
+ });
+
+ it('escapes Markdown code spans when needed', () => {
+ expect(beforeInsert(atwhoInstance, '~a`bug`')).toEqual('~"a\\`bug\\`"');
expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
- expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"');
- expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"');
});
});
diff --git a/spec/frontend/notes/components/discussion_actions_spec.js b/spec/frontend/notes/components/discussion_actions_spec.js
new file mode 100644
index 00000000000..0a52c81571e
--- /dev/null
+++ b/spec/frontend/notes/components/discussion_actions_spec.js
@@ -0,0 +1,104 @@
+import createStore from '~/notes/stores';
+import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
+import { discussionMock } from '../../../javascripts/notes/mock_data';
+import DiscussionActions from '~/notes/components/discussion_actions.vue';
+import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import ResolveDiscussionButton from '~/notes/components/discussion_resolve_button.vue';
+import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
+import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
+
+describe('DiscussionActions', () => {
+ let wrapper;
+ const createComponentFactory = (shallow = true) => props => {
+ const localVue = createLocalVue();
+ const store = createStore();
+ const mountFn = shallow ? shallowMount : mount;
+
+ wrapper = mountFn(DiscussionActions, {
+ localVue,
+ store,
+ propsData: {
+ discussion: discussionMock,
+ isResolving: false,
+ resolveButtonTitle: 'Resolve discussion',
+ resolveWithIssuePath: '/some/issue/path',
+ shouldShowJumpToNextDiscussion: true,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('rendering', () => {
+ const createComponent = createComponentFactory();
+
+ it('renders reply placeholder, resolve discussion button, resolve with issue button and jump to next discussion button', () => {
+ createComponent();
+ expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
+ expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(true);
+ expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(true);
+ expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(true);
+ });
+
+ it('only renders reply placholder if disccusion is not resolvable', () => {
+ const discussion = { ...discussionMock };
+ discussion.resolvable = false;
+ createComponent({ discussion });
+
+ expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
+ expect(wrapper.find(ResolveDiscussionButton).exists()).toBe(false);
+ expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false);
+ expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false);
+ });
+
+ it('does not render resolve with issue button if resolveWithIssuePath is falsy', () => {
+ createComponent({ resolveWithIssuePath: '' });
+
+ expect(wrapper.find(ResolveWithIssueButton).exists()).toBe(false);
+ });
+
+ it('does not render jump to next discussion button if shouldShowJumpToNextDiscussion is false', () => {
+ createComponent({ shouldShowJumpToNextDiscussion: false });
+
+ expect(wrapper.find(JumpToNextDiscussionButton).exists()).toBe(false);
+ });
+ });
+
+ describe('events handling', () => {
+ const createComponent = createComponentFactory(false);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('emits showReplyForm event when clicking on reply placeholder', () => {
+ jest.spyOn(wrapper.vm, '$emit');
+ wrapper
+ .find(ReplyPlaceholder)
+ .find('button')
+ .trigger('click');
+ expect(wrapper.vm.$emit).toHaveBeenCalledWith('showReplyForm');
+ });
+
+ it('emits resolve event when clicking on resolve button', () => {
+ jest.spyOn(wrapper.vm, '$emit');
+ wrapper
+ .find(ResolveDiscussionButton)
+ .find('button')
+ .trigger('click');
+ expect(wrapper.vm.$emit).toHaveBeenCalledWith('resolve');
+ });
+
+ it('emits jumpToNextDiscussion event when clicking on jump to next discussion button', () => {
+ jest.spyOn(wrapper.vm, '$emit');
+ wrapper
+ .find(JumpToNextDiscussionButton)
+ .find('button')
+ .trigger('click');
+ expect(wrapper.vm.$emit).toHaveBeenCalledWith('jumpToNextDiscussion');
+ });
+ });
+});
diff --git a/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js
index 7e9aec84016..f163bdd9913 100644
--- a/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js
+++ b/spec/frontend/pages/admin/abuse_reports/abuse_reports_spec.js
@@ -1,6 +1,9 @@
import $ from 'jquery';
import '~/lib/utils/text_utility';
import AbuseReports from '~/pages/admin/abuse_reports/abuse_reports';
+import { setTestTimeout } from 'helpers/timeout';
+
+setTestTimeout(500);
describe('Abuse Reports', () => {
const FIXTURE = 'abuse_reports/abuse_reports_list.html';
diff --git a/spec/javascripts/boards/board_list_common_spec.js b/spec/javascripts/boards/board_list_common_spec.js
new file mode 100644
index 00000000000..cb337e4cc83
--- /dev/null
+++ b/spec/javascripts/boards/board_list_common_spec.js
@@ -0,0 +1,58 @@
+/* global List */
+/* global ListIssue */
+
+import MockAdapter from 'axios-mock-adapter';
+import Vue from 'vue';
+import axios from '~/lib/utils/axios_utils';
+import Sortable from 'sortablejs';
+import BoardList from '~/boards/components/board_list.vue';
+
+import '~/boards/models/issue';
+import '~/boards/models/list';
+import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
+import boardsStore from '~/boards/stores/boards_store';
+
+window.Sortable = Sortable;
+
+export default function createComponent({ done, listIssueProps = {}, componentProps = {} }) {
+ const el = document.createElement('div');
+
+ document.body.appendChild(el);
+ const mock = new MockAdapter(axios);
+ mock.onAny().reply(boardsMockInterceptor);
+ gl.boardService = mockBoardService();
+ boardsStore.create();
+
+ const BoardListComp = Vue.extend(BoardList);
+ const list = new List(listObj);
+ const issue = new ListIssue({
+ title: 'Testing',
+ id: 1,
+ iid: 1,
+ confidential: false,
+ labels: [],
+ assignees: [],
+ ...listIssueProps,
+ });
+ list.issuesSize = 1;
+ list.issues.push(issue);
+
+ const component = new BoardListComp({
+ el,
+ propsData: {
+ disabled: false,
+ list,
+ issues: list.issues,
+ loading: false,
+ issueLinkBase: '/issues',
+ rootPath: '/',
+ ...componentProps,
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ done();
+ });
+
+ return { component, mock };
+}
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 396fc823ef5..9c9b435d7fd 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -1,60 +1,13 @@
-/* global List */
-/* global ListIssue */
-
import Vue from 'vue';
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import Sortable from 'sortablejs';
-import BoardList from '~/boards/components/board_list.vue';
import eventHub from '~/boards/eventhub';
-import '~/boards/models/issue';
-import '~/boards/models/list';
-import boardsStore from '~/boards/stores/boards_store';
-import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
-
-window.Sortable = Sortable;
+import createComponent from './board_list_common_spec';
describe('Board list component', () => {
let mock;
let component;
beforeEach(done => {
- const el = document.createElement('div');
-
- document.body.appendChild(el);
- mock = new MockAdapter(axios);
- mock.onAny().reply(boardsMockInterceptor);
- gl.boardService = mockBoardService();
- boardsStore.create();
-
- const BoardListComp = Vue.extend(BoardList);
- const list = new List(listObj);
- const issue = new ListIssue({
- title: 'Testing',
- id: 1,
- iid: 1,
- confidential: false,
- labels: [],
- assignees: [],
- });
- list.issuesSize = 1;
- list.issues.push(issue);
-
- component = new BoardListComp({
- el,
- propsData: {
- disabled: false,
- list,
- issues: list.issues,
- loading: false,
- issueLinkBase: '/issues',
- rootPath: '/',
- },
- }).$mount();
-
- Vue.nextTick(() => {
- done();
- });
+ ({ mock, component } = createComponent({ done }));
});
afterEach(() => {
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 6eda5047dd0..a5bf97bdcc2 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -285,4 +285,10 @@ describe('Issue card component', () => {
.catch(done.fail);
});
});
+
+ describe('weights', () => {
+ it('not shows weight component', () => {
+ expect(component.$el.querySelector('.board-card-weight')).toBeNull();
+ });
+ });
});
diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js
index d8a980c874d..5bf6937c92e 100644
--- a/spec/javascripts/monitoring/monitoring_store_spec.js
+++ b/spec/javascripts/monitoring/monitoring_store_spec.js
@@ -32,4 +32,28 @@ describe('MonitoringStore', () => {
it('removes the data if all the values from a query are not defined', () => {
expect(store.groups[1].metrics[0].queries[0].result.length).toEqual(0);
});
+
+ it('assigns queries a metric id', () => {
+ expect(store.groups[1].metrics[0].queries[0].metricId).toEqual('100');
+ });
+
+ it('assigns metric id of null if metric has no id', () => {
+ const noId = MonitoringMock.data.map(group => ({
+ ...group,
+ ...{
+ metrics: group.metrics.map(metric => {
+ const { id, ...metricWithoutId } = metric;
+
+ return metricWithoutId;
+ }),
+ },
+ }));
+ store.storeMetrics(noId);
+
+ store.groups.forEach(group => {
+ group.metrics.forEach(metric => {
+ expect(metric.queries.every(query => query.metricId === null)).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/javascripts/performance_bar/components/detailed_metric_spec.js b/spec/javascripts/performance_bar/components/detailed_metric_spec.js
index e91685e50c5..8a7aa057186 100644
--- a/spec/javascripts/performance_bar/components/detailed_metric_spec.js
+++ b/spec/javascripts/performance_bar/components/detailed_metric_spec.js
@@ -27,8 +27,8 @@ describe('detailedMetric', () => {
describe('when the current request has details', () => {
const requestDetails = [
- { duration: '100', feature: 'find_commit', request: 'abcdef' },
- { duration: '23', feature: 'rebase_in_progress', request: '' },
+ { duration: '100', feature: 'find_commit', request: 'abcdef', backtrace: ['hello', 'world'] },
+ { duration: '23', feature: 'rebase_in_progress', request: '', backtrace: ['world', 'hello'] },
];
beforeEach(() => {
@@ -54,9 +54,11 @@ describe('detailedMetric', () => {
});
it('adds a modal with a table of the details', () => {
- vm.$el.querySelectorAll('.performance-bar-modal td strong').forEach((duration, index) => {
- expect(duration.innerText).toContain(requestDetails[index].duration);
- });
+ vm.$el
+ .querySelectorAll('.performance-bar-modal td:nth-child(1)')
+ .forEach((duration, index) => {
+ expect(duration.innerText).toContain(requestDetails[index].duration);
+ });
vm.$el
.querySelectorAll('.performance-bar-modal td:nth-child(2)')
@@ -65,10 +67,16 @@ describe('detailedMetric', () => {
});
vm.$el
- .querySelectorAll('.performance-bar-modal td:nth-child(3)')
+ .querySelectorAll('.performance-bar-modal td:nth-child(2)')
.forEach((request, index) => {
- expect(request.innerText).toEqual(requestDetails[index].request);
+ expect(request.innerText).toContain(requestDetails[index].request);
});
+
+ expect(vm.$el.querySelector('.text-expander.js-toggle-button')).not.toBeNull();
+
+ vm.$el.querySelectorAll('.performance-bar-modal td:nth-child(2)').forEach(request => {
+ expect(request.innerText).toContain('world');
+ });
});
it('displays the metric name', () => {
diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
index 7c7e58d6bb7..582396275ed 100644
--- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
@@ -51,7 +51,7 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201
statuses[:pending]]
end
- it 'recovers from unique constraint violation only twice' do
+ it 'recovers from unique constraint violation only twice', :quarantine do
allow(described_class::Migratable::Stage)
.to receive(:find_by).and_return(nil)
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
index 62dcd80fad7..e8332b14627 100644
--- a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
let!(:deployment) { create(:deployment, deployable: build) }
context 'and a cluster to deploy to' do
- let(:cluster) { create(:cluster, projects: [build.project]) }
+ let(:cluster) { create(:cluster, :group) }
before do
allow(build.deployment).to receive(:cluster).and_return(cluster)
@@ -33,6 +33,12 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
it { is_expected.to be_falsey }
end
+
+ context 'and cluster is project type' do
+ let(:cluster) { create(:cluster, :project) }
+
+ it { is_expected.to be_falsey }
+ end
end
context 'and no cluster to deploy to' do
@@ -52,7 +58,7 @@ describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
subject { described_class.new(build).complete! }
context 'completion is required' do
- let(:cluster) { create(:cluster, projects: [build.project]) }
+ let(:cluster) { create(:cluster, :group) }
before do
allow(build.deployment).to receive(:cluster).and_return(cluster)
diff --git a/spec/lib/gitlab/correlation_id_spec.rb b/spec/lib/gitlab/correlation_id_spec.rb
deleted file mode 100644
index 584d1f48386..00000000000
--- a/spec/lib/gitlab/correlation_id_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::CorrelationId do
- describe '.use_id' do
- it 'yields when executed' do
- expect { |blk| described_class.use_id('id', &blk) }.to yield_control
- end
-
- it 'stacks correlation ids' do
- described_class.use_id('id1') do
- described_class.use_id('id2') do |current_id|
- expect(current_id).to eq('id2')
- end
- end
- end
-
- it 'for missing correlation id it generates random one' do
- described_class.use_id('id1') do
- described_class.use_id(nil) do |current_id|
- expect(current_id).not_to be_empty
- expect(current_id).not_to eq('id1')
- end
- end
- end
- end
-
- describe '.current_id' do
- subject { described_class.current_id }
-
- it 'returns last correlation id' do
- described_class.use_id('id1') do
- described_class.use_id('id2') do
- is_expected.to eq('id2')
- end
- end
- end
- end
-
- describe '.current_or_new_id' do
- subject { described_class.current_or_new_id }
-
- context 'when correlation id is set' do
- it 'returns last correlation id' do
- described_class.use_id('id1') do
- is_expected.to eq('id1')
- end
- end
- end
-
- context 'when correlation id is missing' do
- it 'returns a new correlation id' do
- expect(described_class).to receive(:new_id)
- .and_call_original
-
- is_expected.not_to be_empty
- end
- end
- end
-
- describe '.ids' do
- subject { described_class.send(:ids) }
-
- it 'returns empty list if not correlation is used' do
- is_expected.to be_empty
- end
-
- it 'returns list if correlation ids are used' do
- described_class.use_id('id1') do
- described_class.use_id('id2') do
- is_expected.to eq(%w(id1 id2))
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 507bf222810..25052a79916 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -380,7 +380,32 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
- describe '#batch_by_oid' do
+ shared_examples '.batch_by_oid' do
+ context 'with multiple OIDs' do
+ let(:oids) { [SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID] }
+
+ it 'returns multiple commits' do
+ commits = described_class.batch_by_oid(repository, oids)
+
+ expect(commits.count).to eq(2)
+ expect(commits).to all( be_a(Gitlab::Git::Commit) )
+ expect(commits.first.sha).to eq(SeedRepo::Commit::ID)
+ expect(commits.second.sha).to eq(SeedRepo::FirstCommit::ID)
+ end
+ end
+
+ context 'when oids is empty' do
+ it 'returns empty commits' do
+ commits = described_class.batch_by_oid(repository, [])
+
+ expect(commits.count).to eq(0)
+ end
+ end
+ end
+
+ describe '.batch_by_oid with Gitaly enabled' do
+ it_should_behave_like '.batch_by_oid'
+
context 'when oids is empty' do
it 'makes no Gitaly request' do
expect(Gitlab::GitalyClient).not_to receive(:call)
@@ -390,6 +415,16 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
+ describe '.batch_by_oid with Rugged enabled', :enable_rugged do
+ it_should_behave_like '.batch_by_oid'
+
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+
+ described_class.batch_by_oid(repository, [SeedRepo::Commit::ID])
+ end
+ end
+
shared_examples 'extracting commit signature' do
context 'when the commit is signed' do
let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb
index cff7dd58c8c..d3d9fe9948a 100644
--- a/spec/lib/gitlab/json_logger_spec.rb
+++ b/spec/lib/gitlab/json_logger_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::JsonLogger do
describe '#format_message' do
before do
- allow(Gitlab::CorrelationId).to receive(:current_id).and_return('new-correlation-id')
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('new-correlation-id')
end
it 'formats strings' do
diff --git a/spec/lib/gitlab/sentry_spec.rb b/spec/lib/gitlab/sentry_spec.rb
index 1128eaf8560..ae522a588ee 100644
--- a/spec/lib/gitlab/sentry_spec.rb
+++ b/spec/lib/gitlab/sentry_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::Sentry do
context 'when exceptions should not be raised' do
before do
allow(described_class).to receive(:should_raise_for_dev?).and_return(false)
- allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid')
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
end
it 'logs the exception with all attributes passed' do
@@ -65,7 +65,7 @@ describe Gitlab::Sentry do
before do
allow(described_class).to receive(:enabled?).and_return(true)
- allow(Gitlab::CorrelationId).to receive(:current_id).and_return('cid')
+ allow(Labkit::Correlation::CorrelationId).to receive(:current_id).and_return('cid')
end
it 'calls Raven.capture_exception' do
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
index a138ad7c910..0ff694d409b 100644
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/correlation_injector_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::SidekiqMiddleware::CorrelationInjector do
it 'injects into payload the correlation id' do
expect_any_instance_of(described_class).to receive(:call).and_call_original
- Gitlab::CorrelationId.use_id('new-correlation-id') do
+ Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
TestWorker.perform_async(1234)
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
index 94ae4ffa184..8410467ef1f 100644
--- a/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/correlation_logger_spec.rb
@@ -23,7 +23,7 @@ describe Gitlab::SidekiqMiddleware::CorrelationLogger do
expect_any_instance_of(described_class).to receive(:call).and_call_original
expect_any_instance_of(TestWorker).to receive(:perform).with(1234) do
- expect(Gitlab::CorrelationId.current_id).to eq('new-correlation-id')
+ expect(Labkit::Correlation::CorrelationId.current_id).to eq('new-correlation-id')
end
Sidekiq::Client.push(
diff --git a/spec/lib/gitlab/tracing/factory_spec.rb b/spec/lib/gitlab/tracing/factory_spec.rb
deleted file mode 100644
index 945490f0988..00000000000
--- a/spec/lib/gitlab/tracing/factory_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::Tracing::Factory do
- describe '.create_tracer' do
- let(:service_name) { 'rspec' }
-
- context "when tracing is not configured" do
- it 'ignores null connection strings' do
- expect(described_class.create_tracer(service_name, nil)).to be_nil
- end
-
- it 'ignores empty connection strings' do
- expect(described_class.create_tracer(service_name, '')).to be_nil
- end
-
- it 'ignores unknown implementations' do
- expect(described_class.create_tracer(service_name, 'opentracing://invalid_driver')).to be_nil
- end
-
- it 'ignores invalid connection strings' do
- expect(described_class.create_tracer(service_name, 'open?tracing')).to be_nil
- end
- end
-
- context "when tracing is configured with jaeger" do
- let(:mock_tracer) { double('tracer') }
-
- it 'processes default connections' do
- expect(Gitlab::Tracing::JaegerFactory).to receive(:create_tracer).with(service_name, {}).and_return(mock_tracer)
-
- expect(described_class.create_tracer(service_name, 'opentracing://jaeger')).to be(mock_tracer)
- end
-
- it 'processes connections with parameters' do
- expect(Gitlab::Tracing::JaegerFactory).to receive(:create_tracer).with(service_name, { a: '1', b: '2', c: '3' }).and_return(mock_tracer)
-
- expect(described_class.create_tracer(service_name, 'opentracing://jaeger?a=1&b=2&c=3')).to be(mock_tracer)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb b/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb
deleted file mode 100644
index 7f5aecb7baa..00000000000
--- a/spec/lib/gitlab/tracing/grpc_interceptor_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::Tracing::GRPCInterceptor do
- subject { described_class.instance }
-
- shared_examples_for "a grpc interceptor method" do
- let(:custom_error) { Class.new(StandardError) }
-
- it 'yields' do
- expect { |b| method.call(kwargs, &b) }.to yield_control
- end
-
- it 'propagates exceptions' do
- expect { method.call(kwargs) { raise custom_error } }.to raise_error(custom_error)
- end
- end
-
- describe '#request_response' do
- let(:method) { subject.method(:request_response) }
- let(:kwargs) { { request: {}, call: {}, method: 'grc_method', metadata: {} } }
-
- it_behaves_like 'a grpc interceptor method'
- end
-
- describe '#client_streamer' do
- let(:method) { subject.method(:client_streamer) }
- let(:kwargs) { { requests: [], call: {}, method: 'grc_method', metadata: {} } }
-
- it_behaves_like 'a grpc interceptor method'
- end
-
- describe '#server_streamer' do
- let(:method) { subject.method(:server_streamer) }
- let(:kwargs) { { request: {}, call: {}, method: 'grc_method', metadata: {} } }
-
- it_behaves_like 'a grpc interceptor method'
- end
-
- describe '#bidi_streamer' do
- let(:method) { subject.method(:bidi_streamer) }
- let(:kwargs) { { requests: [], call: {}, method: 'grc_method', metadata: {} } }
-
- it_behaves_like 'a grpc interceptor method'
- end
-end
diff --git a/spec/lib/gitlab/tracing/jaeger_factory_spec.rb b/spec/lib/gitlab/tracing/jaeger_factory_spec.rb
deleted file mode 100644
index 3d6a007cfd9..00000000000
--- a/spec/lib/gitlab/tracing/jaeger_factory_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::Tracing::JaegerFactory do
- describe '.create_tracer' do
- let(:service_name) { 'rspec' }
-
- shared_examples_for 'a jaeger tracer' do
- it 'responds to active_span methods' do
- expect(tracer).to respond_to(:active_span)
- end
-
- it 'yields control' do
- expect { |b| tracer.start_active_span('operation_name', &b) }.to yield_control
- end
- end
-
- context 'processes default connections' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, {}) }
- end
- end
-
- context 'handles debug options' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { debug: "1" }) }
- end
- end
-
- context 'handles const sampler' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { sampler: "const", sampler_param: "1" }) }
- end
- end
-
- context 'handles probabilistic sampler' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { sampler: "probabilistic", sampler_param: "0.5" }) }
- end
- end
-
- context 'handles http_endpoint configurations' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { http_endpoint: "http://localhost:1234" }) }
- end
- end
-
- context 'handles udp_endpoint configurations' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { udp_endpoint: "localhost:4321" }) }
- end
- end
-
- context 'ignores invalid parameters' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { invalid: "true" }) }
- end
- end
-
- context 'accepts the debug parameter when strict_parser is set' do
- it_behaves_like 'a jaeger tracer' do
- let(:tracer) { described_class.create_tracer(service_name, { debug: "1", strict_parsing: "1" }) }
- end
- end
-
- it 'rejects invalid parameters when strict_parser is set' do
- expect { described_class.create_tracer(service_name, { invalid: "true", strict_parsing: "1" }) }.to raise_error(StandardError)
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/rack_middleware_spec.rb b/spec/lib/gitlab/tracing/rack_middleware_spec.rb
deleted file mode 100644
index 13d4d8a89f7..00000000000
--- a/spec/lib/gitlab/tracing/rack_middleware_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Tracing::RackMiddleware do
- using RSpec::Parameterized::TableSyntax
-
- describe '#call' do
- context 'for normal middleware flow' do
- let(:fake_app) { -> (env) { fake_app_response } }
- subject { described_class.new(fake_app) }
- let(:request) { }
-
- context 'for 200 responses' do
- let(:fake_app_response) { [200, { 'Content-Type': 'text/plain' }, ['OK']] }
-
- it 'delegates correctly' do
- expect(subject.call(Rack::MockRequest.env_for("/"))).to eq(fake_app_response)
- end
- end
-
- context 'for 500 responses' do
- let(:fake_app_response) { [500, { 'Content-Type': 'text/plain' }, ['Error']] }
-
- it 'delegates correctly' do
- expect(subject.call(Rack::MockRequest.env_for("/"))).to eq(fake_app_response)
- end
- end
- end
-
- context 'when an application is raising an exception' do
- let(:custom_error) { Class.new(StandardError) }
- let(:fake_app) { ->(env) { raise custom_error } }
-
- subject { described_class.new(fake_app) }
-
- it 'delegates propagates exceptions correctly' do
- expect { subject.call(Rack::MockRequest.env_for("/")) }.to raise_error(custom_error)
- end
- end
- end
-
- describe '.build_sanitized_url_from_env' do
- def env_for_url(url)
- env = Rack::MockRequest.env_for(input_url)
- env['action_dispatch.parameter_filter'] = [/token/]
-
- env
- end
-
- where(:input_url, :output_url) do
- '/gitlab-org/gitlab-ce' | 'http://example.org/gitlab-org/gitlab-ce'
- '/gitlab-org/gitlab-ce?safe=1' | 'http://example.org/gitlab-org/gitlab-ce?safe=1'
- '/gitlab-org/gitlab-ce?private_token=secret' | 'http://example.org/gitlab-org/gitlab-ce?private_token=%5BFILTERED%5D'
- '/gitlab-org/gitlab-ce?mixed=1&private_token=secret' | 'http://example.org/gitlab-org/gitlab-ce?mixed=1&private_token=%5BFILTERED%5D'
- end
-
- with_them do
- it { expect(described_class.build_sanitized_url_from_env(env_for_url(input_url))).to eq(output_url) }
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb b/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb
deleted file mode 100644
index 0bbaf5968ed..00000000000
--- a/spec/lib/gitlab/tracing/rails/action_view_subscriber_spec.rb
+++ /dev/null
@@ -1,147 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require 'rspec-parameterized'
-
-describe Gitlab::Tracing::Rails::ActionViewSubscriber do
- using RSpec::Parameterized::TableSyntax
-
- shared_examples 'an actionview notification' do
- it 'notifies the tracer when the hash contains null values' do
- expect(subject).to receive(:postnotify_span).with(notification_name, start, finish, tags: expected_tags, exception: exception)
-
- subject.public_send(notify_method, start, finish, payload)
- end
-
- it 'notifies the tracer when the payload is missing values' do
- expect(subject).to receive(:postnotify_span).with(notification_name, start, finish, tags: expected_tags, exception: exception)
-
- subject.public_send(notify_method, start, finish, payload.compact)
- end
-
- it 'does not throw exceptions when with the default tracer' do
- expect { subject.public_send(notify_method, start, finish, payload) }.not_to raise_error
- end
- end
-
- describe '.instrument' do
- it 'is unsubscribeable' do
- unsubscribe = described_class.instrument
-
- expect(unsubscribe).not_to be_nil
- expect { unsubscribe.call }.not_to raise_error
- end
- end
-
- describe '#notify_render_template' do
- subject { described_class.new }
- let(:start) { Time.now }
- let(:finish) { Time.now }
- let(:notification_name) { 'render_template' }
- let(:notify_method) { :notify_render_template }
-
- where(:identifier, :layout, :exception) do
- nil | nil | nil
- "" | nil | nil
- "show.haml" | nil | nil
- nil | "" | nil
- nil | "layout.haml" | nil
- nil | nil | StandardError.new
- end
-
- with_them do
- let(:payload) do
- {
- exception: exception,
- identifier: identifier,
- layout: layout
- }
- end
-
- let(:expected_tags) do
- {
- 'component' => 'ActionView',
- 'template.id' => identifier,
- 'template.layout' => layout
- }
- end
-
- it_behaves_like 'an actionview notification'
- end
- end
-
- describe '#notify_render_collection' do
- subject { described_class.new }
- let(:start) { Time.now }
- let(:finish) { Time.now }
- let(:notification_name) { 'render_collection' }
- let(:notify_method) { :notify_render_collection }
-
- where(
- :identifier, :count, :expected_count, :cache_hits, :expected_cache_hits, :exception) do
- nil | nil | 0 | nil | 0 | nil
- "" | nil | 0 | nil | 0 | nil
- "show.haml" | nil | 0 | nil | 0 | nil
- nil | 0 | 0 | nil | 0 | nil
- nil | 1 | 1 | nil | 0 | nil
- nil | nil | 0 | 0 | 0 | nil
- nil | nil | 0 | 1 | 1 | nil
- nil | nil | 0 | nil | 0 | StandardError.new
- end
-
- with_them do
- let(:payload) do
- {
- exception: exception,
- identifier: identifier,
- count: count,
- cache_hits: cache_hits
- }
- end
-
- let(:expected_tags) do
- {
- 'component' => 'ActionView',
- 'template.id' => identifier,
- 'template.count' => expected_count,
- 'template.cache.hits' => expected_cache_hits
- }
- end
-
- it_behaves_like 'an actionview notification'
- end
- end
-
- describe '#notify_render_partial' do
- subject { described_class.new }
- let(:start) { Time.now }
- let(:finish) { Time.now }
- let(:notification_name) { 'render_partial' }
- let(:notify_method) { :notify_render_partial }
-
- where(:identifier, :exception) do
- nil | nil
- "" | nil
- "show.haml" | nil
- nil | StandardError.new
- end
-
- with_them do
- let(:payload) do
- {
- exception: exception,
- identifier: identifier
- }
- end
-
- let(:expected_tags) do
- {
- 'component' => 'ActionView',
- 'template.id' => identifier
- }
- end
-
- it_behaves_like 'an actionview notification'
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb b/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb
deleted file mode 100644
index 7bd0875fa68..00000000000
--- a/spec/lib/gitlab/tracing/rails/active_record_subscriber_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-require 'rspec-parameterized'
-
-describe Gitlab::Tracing::Rails::ActiveRecordSubscriber do
- using RSpec::Parameterized::TableSyntax
-
- describe '.instrument' do
- it 'is unsubscribeable' do
- unsubscribe = described_class.instrument
-
- expect(unsubscribe).not_to be_nil
- expect { unsubscribe.call }.not_to raise_error
- end
- end
-
- describe '#notify' do
- subject { described_class.new }
- let(:start) { Time.now }
- let(:finish) { Time.now }
-
- where(:name, :operation_name, :exception, :connection_id, :cached, :cached_response, :sql) do
- nil | "active_record:sqlquery" | nil | nil | nil | false | nil
- "" | "active_record:sqlquery" | nil | nil | nil | false | nil
- "User Load" | "active_record:User Load" | nil | nil | nil | false | nil
- "Repo Load" | "active_record:Repo Load" | StandardError.new | nil | nil | false | nil
- nil | "active_record:sqlquery" | nil | 123 | nil | false | nil
- nil | "active_record:sqlquery" | nil | nil | false | false | nil
- nil | "active_record:sqlquery" | nil | nil | true | true | nil
- nil | "active_record:sqlquery" | nil | nil | true | true | "SELECT * FROM users"
- end
-
- with_them do
- def payload
- {
- name: name,
- exception: exception,
- connection_id: connection_id,
- cached: cached,
- sql: sql
- }
- end
-
- def expected_tags
- {
- "component" => "ActiveRecord",
- "span.kind" => "client",
- "db.type" => "sql",
- "db.connection_id" => connection_id,
- "db.cached" => cached_response,
- "db.statement" => sql
- }
- end
-
- it 'notifies the tracer when the hash contains null values' do
- expect(subject).to receive(:postnotify_span).with(operation_name, start, finish, tags: expected_tags, exception: exception)
-
- subject.notify(start, finish, payload)
- end
-
- it 'notifies the tracer when the payload is missing values' do
- expect(subject).to receive(:postnotify_span).with(operation_name, start, finish, tags: expected_tags, exception: exception)
-
- subject.notify(start, finish, payload.compact)
- end
-
- it 'does not throw exceptions when with the default tracer' do
- expect { subject.notify(start, finish, payload) }.not_to raise_error
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb b/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb
deleted file mode 100644
index 3755860b5ba..00000000000
--- a/spec/lib/gitlab/tracing/sidekiq/client_middleware_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::Tracing::Sidekiq::ClientMiddleware do
- describe '#call' do
- let(:worker_class) { 'test_worker_class' }
- let(:job) do
- {
- 'class' => "jobclass",
- 'queue' => "jobqueue",
- 'retry' => 0,
- 'args' => %w{1 2 3}
- }
- end
- let(:queue) { 'test_queue' }
- let(:redis_pool) { double("redis_pool") }
- let(:custom_error) { Class.new(StandardError) }
- let(:span) { OpenTracing.start_span('test', ignore_active_scope: true) }
-
- subject { described_class.new }
-
- it 'yields' do
- expect(subject).to receive(:in_tracing_span).with(
- operation_name: "sidekiq:jobclass",
- tags: {
- "component" => "sidekiq",
- "span.kind" => "client",
- "sidekiq.queue" => "jobqueue",
- "sidekiq.jid" => nil,
- "sidekiq.retry" => "0",
- "sidekiq.args" => "1, 2, 3"
- }
- ).and_yield(span)
-
- expect { |b| subject.call(worker_class, job, queue, redis_pool, &b) }.to yield_control
- end
-
- it 'propagates exceptions' do
- expect { subject.call(worker_class, job, queue, redis_pool) { raise custom_error } }.to raise_error(custom_error)
- end
- end
-end
diff --git a/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb b/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb
deleted file mode 100644
index c3087de785a..00000000000
--- a/spec/lib/gitlab/tracing/sidekiq/server_middleware_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-require 'fast_spec_helper'
-
-describe Gitlab::Tracing::Sidekiq::ServerMiddleware do
- describe '#call' do
- let(:worker_class) { 'test_worker_class' }
- let(:job) do
- {
- 'class' => "jobclass",
- 'queue' => "jobqueue",
- 'retry' => 0,
- 'args' => %w{1 2 3}
- }
- end
- let(:queue) { 'test_queue' }
- let(:custom_error) { Class.new(StandardError) }
- let(:span) { OpenTracing.start_span('test', ignore_active_scope: true) }
- subject { described_class.new }
-
- it 'yields' do
- expect(subject).to receive(:in_tracing_span).with(
- hash_including(
- operation_name: "sidekiq:jobclass",
- tags: {
- "component" => "sidekiq",
- "span.kind" => "server",
- "sidekiq.queue" => "jobqueue",
- "sidekiq.jid" => nil,
- "sidekiq.retry" => "0",
- "sidekiq.args" => "1, 2, 3"
- }
- )
- ).and_yield(span)
-
- expect { |b| subject.call(worker_class, job, queue, &b) }.to yield_control
- end
-
- it 'propagates exceptions' do
- expect { subject.call(worker_class, job, queue) { raise custom_error } }.to raise_error(custom_error)
- end
- end
-end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 5e68f2634da..405b5ad691c 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -109,7 +109,7 @@ describe Clusters::Applications::Knative do
subject { knative.install_command }
it 'is initialized with latest version' do
- expect(subject.version).to eq('0.3.0')
+ expect(subject.version).to eq('0.5.0')
end
it_behaves_like 'a command'
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 387d1221c76..95a4b0f6d71 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -63,6 +63,11 @@ describe Namespace do
end
end
+ describe 'delegate' do
+ it { is_expected.to delegate_method(:name).to(:owner).with_prefix.with_arguments(allow_nil: true) }
+ it { is_expected.to delegate_method(:avatar_url).to(:owner).with_arguments(allow_nil: true) }
+ end
+
describe "Respond to" do
it { is_expected.to respond_to(:human_name) }
it { is_expected.to respond_to(:to_param) }
@@ -801,4 +806,21 @@ describe Namespace do
end
end
end
+
+ describe '#user?' do
+ subject { namespace.user? }
+
+ context 'when type is a user' do
+ let(:user) { create(:user) }
+ let(:namespace) { user.namespace }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when type is a group' do
+ let(:namespace) { create(:group) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
end
diff --git a/spec/rack_servers/configs/puma.rb b/spec/rack_servers/configs/puma.rb
deleted file mode 100644
index d6b6d83d648..00000000000
--- a/spec/rack_servers/configs/puma.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-# Note: this file is used for testing puma in `spec/rack_servers/puma_spec.rb` only
-# Note: as per the convention in `config/puma.example.development.rb`,
-# this file will replace `/home/git` with the actual working directory
-
-directory '/home/git'
-threads 1, 10
-queue_requests false
-pidfile '/home/git/gitlab/tmp/pids/puma.pid'
-bind 'unix:///home/git/gitlab/tmp/tests/puma.socket'
-workers 1
-preload_app!
-worker_timeout 60
-
-require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
-require_relative "/home/git/gitlab/lib/gitlab/cluster/puma_worker_killer_initializer"
-
-before_fork do
- Gitlab::Cluster::PumaWorkerKillerInitializer.start @config.options
- Gitlab::Cluster::LifecycleEvents.do_before_fork
-end
-
-Gitlab::Cluster::LifecycleEvents.set_puma_options @config.options
-on_worker_boot do
- Gitlab::Cluster::LifecycleEvents.do_worker_start
- File.write('/home/git/gitlab/tmp/tests/puma-worker-ready', Process.pid)
-end
-
-on_restart do
- Gitlab::Cluster::LifecycleEvents.do_master_restart
-end
diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb
index 891df4f1a66..8290473821c 100644
--- a/spec/rack_servers/puma_spec.rb
+++ b/spec/rack_servers/puma_spec.rb
@@ -1,20 +1,20 @@
# frozen_string_literal: true
-require 'fileutils'
+require 'spec_helper'
+require 'fileutils'
require 'excon'
-require 'spec_helper'
-
describe 'Puma' do
before(:all) do
- project_root = File.expand_path('../..', __dir__)
-
- config_lines = File.read('spec/rack_servers/configs/puma.rb')
- .gsub('/home/git/gitlab', project_root)
- .gsub('/home/git', project_root)
-
- config_path = File.join(project_root, "tmp/tests/puma.rb")
+ project_root = Rails.root.to_s
+ config_lines = File.read(Rails.root.join('config/puma.example.development.rb'))
+ .gsub('config.ru', File.join(__dir__, 'configs/config.ru'))
+ .gsub('workers 2', 'workers 1')
+ .gsub('/home/git/gitlab.socket', File.join(project_root, 'tmp/tests/puma.socket'))
+ .gsub('on_worker_boot do', "on_worker_boot do\nFile.write('#{File.join(project_root, 'tmp/tests/puma-worker-ready')}', Process.pid)")
+ .gsub(%r{/home/git(/gitlab)?}, project_root)
+ config_path = File.join(project_root, 'tmp/tests/puma.rb')
@socket_path = File.join(project_root, 'tmp/tests/puma.socket')
File.write(config_path, config_lines)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index a0c64d295c0..25a312cb734 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -251,7 +251,7 @@ describe API::Helpers do
correlation_id: 'new-correlation-id'
}, extra: {})
- Gitlab::CorrelationId.use_id('new-correlation-id') do
+ Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
handle_api_exception(exception)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 352ea448c00..577f61ae8d0 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1047,7 +1047,6 @@ describe API::Projects do
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
expect(json_response['owner']).to be_a Hash
- expect(json_response['owner']).to be_a Hash
expect(json_response['name']).to eq(project.name)
expect(json_response['path']).to be_present
expect(json_response['issues_enabled']).to be_present
@@ -1135,7 +1134,9 @@ describe API::Projects do
'path' => user.namespace.path,
'kind' => user.namespace.kind,
'full_path' => user.namespace.full_path,
- 'parent_id' => nil
+ 'parent_id' => nil,
+ 'avatar_url' => user.avatar_url,
+ 'web_url' => Gitlab::Routing.url_helpers.user_url(user)
})
end
@@ -1337,6 +1338,37 @@ describe API::Projects do
end
end
end
+
+ context 'when project belongs to a group namespace' do
+ let(:group) { create(:group, :with_avatar) }
+ let(:project) { create(:project, namespace: group) }
+ let!(:project_member) { create(:project_member, :developer, user: user, project: project) }
+
+ it 'returns group web_url and avatar_url' do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+
+ group_data = json_response['namespace']
+ expect(group_data['web_url']).to eq(group.web_url)
+ expect(group_data['avatar_url']).to eq(group.avatar_url)
+ end
+ end
+
+ context 'when project belongs to a user namespace' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
+ it 'returns user web_url and avatar_url' do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+
+ user_data = json_response['namespace']
+ expect(user_data['web_url']).to eq("http://localhost/#{user.username}")
+ expect(user_data['avatar_url']).to eq(user.avatar_url)
+ end
+ end
end
context 'with external authorization' do
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 3585a827838..29515709a74 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -168,6 +168,32 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
+ context 'when access_level is provided for Runner' do
+ context 'when access_level is set to ref_protected' do
+ it 'creates runner' do
+ post api('/runners'), params: {
+ token: registration_token,
+ access_level: 'ref_protected'
+ }
+
+ expect(response).to have_gitlab_http_status 201
+ expect(Ci::Runner.first.ref_protected?).to be true
+ end
+ end
+
+ context 'when access_level is set to not_protected' do
+ it 'creates runner' do
+ post api('/runners'), params: {
+ token: registration_token,
+ access_level: 'not_protected'
+ }
+
+ expect(response).to have_gitlab_http_status 201
+ expect(Ci::Runner.first.ref_protected?).to be false
+ end
+ end
+ end
+
context 'when maximum job timeout is specified' do
it 'creates runner' do
post api('/runners'), params: {
diff --git a/spec/services/clusters/refresh_service_spec.rb b/spec/services/clusters/refresh_service_spec.rb
index 58ab3c3cf73..9e442ebf4e9 100644
--- a/spec/services/clusters/refresh_service_spec.rb
+++ b/spec/services/clusters/refresh_service_spec.rb
@@ -93,14 +93,32 @@ describe Clusters::RefreshService do
let(:group) { cluster.group }
let(:project) { create(:project, group: group) }
- include_examples 'creates a kubernetes namespace'
+ context 'when ci_preparing_state feature flag is enabled' do
+ include_examples 'does not create a kubernetes namespace'
- context 'when project already has kubernetes namespace' do
+ context 'when project already has kubernetes namespace' do
+ before do
+ create(:cluster_kubernetes_namespace, project: project, cluster: cluster)
+ end
+
+ include_examples 'does not create a kubernetes namespace'
+ end
+ end
+
+ context 'when ci_preparing_state feature flag is disabled' do
before do
- create(:cluster_kubernetes_namespace, project: project, cluster: cluster)
+ stub_feature_flags(ci_preparing_state: false)
end
- include_examples 'does not create a kubernetes namespace'
+ include_examples 'creates a kubernetes namespace'
+
+ context 'when project already has kubernetes namespace' do
+ before do
+ create(:cluster_kubernetes_namespace, project: project, cluster: cluster)
+ end
+
+ include_examples 'does not create a kubernetes namespace'
+ end
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 60db3e1bc46..8ca4c172707 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -83,6 +83,7 @@ RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :feature
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
+ config.include WaitHelpers, type: :feature
config.include EmailHelpers, :mailer, type: :mailer
config.include Warden::Test::Helpers, type: :request
config.include Gitlab::Routing, type: :routing
diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb
index c7f878b7371..45b9faa0fea 100644
--- a/spec/support/helpers/wait_for_requests.rb
+++ b/spec/support/helpers/wait_for_requests.rb
@@ -50,20 +50,6 @@ module WaitForRequests
finished_all_vue_resource_requests?
end
- # Waits until the passed block returns true
- def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01)
- wait_until = Time.now + max_wait_time.seconds
- loop do
- break if yield
-
- if Time.now > wait_until
- raise "Condition not met: #{condition_name}"
- else
- sleep(polling_interval)
- end
- end
- end
-
def finished_all_vue_resource_requests?
Capybara.page.evaluate_script('window.activeVueResources || 0').zero?
end
diff --git a/spec/support/helpers/wait_helpers.rb b/spec/support/helpers/wait_helpers.rb
new file mode 100644
index 00000000000..7e8e25798e8
--- /dev/null
+++ b/spec/support/helpers/wait_helpers.rb
@@ -0,0 +1,20 @@
+module WaitHelpers
+ extend self
+
+ # Waits until the passed block returns true
+ def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01, reload: false)
+ wait_until = Time.now + max_wait_time.seconds
+ loop do
+ result = yield
+ break if result
+
+ page.refresh if reload
+
+ if Time.now > wait_until
+ raise "Condition not met: #{condition_name}"
+ else
+ sleep(polling_interval)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb
index dd1676a08e2..657c2a60d24 100644
--- a/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issue/remove_due_date_quick_action_shared_examples.rb
@@ -11,7 +11,7 @@ shared_examples 'remove_due_date quick action' do
visit project_issue_path(project, issue)
page.within '.due_date' do
- expect(page).to have_content 'No due date'
+ expect(page).to have_content 'None'
end
end
end
diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
new file mode 100644
index 00000000000..a7d3dc09fd4
--- /dev/null
+++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'notify/pipeline_failed_email.text.erb' do
+ include Devise::Test::ControllerHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ :failed,
+ project: project,
+ user: user,
+ ref: project.default_branch,
+ sha: project.commit.sha)
+ end
+
+ before do
+ assign(:project, project)
+ assign(:pipeline, pipeline)
+ assign(:merge_request, merge_request)
+ end
+
+ it 'renders the email correctly' do
+ job = create(:ci_build, :failed, pipeline: pipeline, project: pipeline.project)
+
+ render
+
+ expect(rendered).to have_content('Your pipeline has failed')
+ expect(rendered).to have_content(pipeline.project.name)
+ expect(rendered).to have_content(pipeline.git_commit_message.truncate(50))
+ expect(rendered).to have_content(pipeline.commit.author_name)
+ expect(rendered).to have_content("##{pipeline.id}")
+ expect(rendered).to have_content(pipeline.user.name)
+ expect(rendered).to have_content("/-/jobs/#{job.id}/raw")
+ end
+end
diff --git a/spec/views/shared/_label_row.html.haml.rb b/spec/views/shared/_label_row.html.haml.rb
new file mode 100644
index 00000000000..a58d5efc1e3
--- /dev/null
+++ b/spec/views/shared/_label_row.html.haml.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe 'shared/_label_row.html.haml' do
+ label_types = {
+ 'project label': :label,
+ 'group label': :group_label
+ }
+
+ label_types.each do |label_type, label_factory|
+ let!(:label) { create(label_factory) }
+
+ context "for a #{label_type}" do
+ it 'has a non-linked label title' do
+ render 'shared/label_row', label: label
+
+ expect(rendered).not_to have_css('a', text: label.title)
+ end
+
+ it "has Issues link for #{label_type}" do
+ render 'shared/label_row', label: label
+
+ expect(rendered).to have_css('a', text: 'Issues')
+ end
+
+ it "has Merge request link for #{label_type}" do
+ render 'shared/label_row', label: label
+
+ expect(rendered).to have_css('a', text: 'Merge requests')
+ end
+ end
+ end
+end
diff --git a/spec/workers/cluster_configure_worker_spec.rb b/spec/workers/cluster_configure_worker_spec.rb
index 83f76809435..bdb8e0e9c84 100644
--- a/spec/workers/cluster_configure_worker_spec.rb
+++ b/spec/workers/cluster_configure_worker_spec.rb
@@ -10,25 +10,35 @@ describe ClusterConfigureWorker, '#perform' do
stub_feature_flags(ci_preparing_state: ci_preparing_state_enabled)
end
- context 'when group cluster' do
- let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
- let(:group) { cluster.group }
+ shared_examples 'configured cluster' do
+ it 'creates a namespace' do
+ expect(Clusters::RefreshService).to receive(:create_or_update_namespaces_for_cluster).with(cluster).once
- context 'when group has no projects' do
- it 'does not create a namespace' do
- expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:execute)
+ worker.perform(cluster.id)
+ end
+ end
- worker.perform(cluster.id)
- end
+ shared_examples 'unconfigured cluster' do
+ it 'does not create a namespace' do
+ expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster)
+
+ worker.perform(cluster.id)
end
+ end
+
+ context 'group cluster' do
+ let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
+ let(:group) { cluster.group }
context 'when group has a project' do
let!(:project) { create(:project, group: group) }
- it 'creates a namespace for the project' do
- expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute).once
+ it_behaves_like 'configured cluster'
+
+ context 'ci_preparing_state feature is enabled' do
+ let(:ci_preparing_state_enabled) { true }
- worker.perform(cluster.id)
+ it_behaves_like 'unconfigured cluster'
end
end
@@ -36,32 +46,26 @@ describe ClusterConfigureWorker, '#perform' do
let!(:subgroup) { create(:group, parent: group) }
let!(:project) { create(:project, group: subgroup) }
- it 'creates a namespace for the project' do
- expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute).once
+ it_behaves_like 'configured cluster'
- worker.perform(cluster.id)
+ context 'ci_preparing_state feature is enabled' do
+ let(:ci_preparing_state_enabled) { true }
+
+ it_behaves_like 'unconfigured cluster'
end
end
end
context 'when provider type is gcp' do
- let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
-
- it 'configures kubernetes platform' do
- expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
+ let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
- described_class.new.perform(cluster.id)
- end
+ it_behaves_like 'configured cluster'
end
context 'when provider type is user' do
- let(:cluster) { create(:cluster, :project, :provided_by_user) }
+ let!(:cluster) { create(:cluster, :project, :provided_by_user) }
- it 'configures kubernetes platform' do
- expect_any_instance_of(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute)
-
- described_class.new.perform(cluster.id)
- end
+ it_behaves_like 'configured cluster'
end
context 'when cluster does not exist' do
@@ -71,15 +75,4 @@ describe ClusterConfigureWorker, '#perform' do
described_class.new.perform(123)
end
end
-
- context 'ci_preparing_state feature is enabled' do
- let(:cluster) { create(:cluster) }
- let(:ci_preparing_state_enabled) { true }
-
- it 'does not configure the cluster' do
- expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster)
-
- described_class.new.perform(cluster.id)
- end
- end
end
diff --git a/spec/workers/cluster_project_configure_worker_spec.rb b/spec/workers/cluster_project_configure_worker_spec.rb
index afdea55adf4..2ac9d0f61b4 100644
--- a/spec/workers/cluster_project_configure_worker_spec.rb
+++ b/spec/workers/cluster_project_configure_worker_spec.rb
@@ -4,18 +4,11 @@ require 'spec_helper'
describe ClusterProjectConfigureWorker, '#perform' do
let(:worker) { described_class.new }
+ let(:cluster) { create(:cluster, :project) }
- context 'ci_preparing_state feature is enabled' do
- let(:cluster) { create(:cluster) }
+ it 'configures the cluster' do
+ expect(Clusters::RefreshService).to receive(:create_or_update_namespaces_for_project)
- before do
- stub_feature_flags(ci_preparing_state: true)
- end
-
- it 'does not configure the cluster' do
- expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_project)
-
- described_class.new.perform(cluster.id)
- end
+ described_class.new.perform(cluster.projects.first.id)
end
end