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--Gemfile1
-rw-r--r--Gemfile.lock30
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue7
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue8
-rw-r--r--app/assets/javascripts/boards/components/issue_due_date.vue6
-rw-r--r--app/assets/javascripts/boards/components/issue_time_estimate.vue2
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.vue4
-rw-r--r--app/assets/javascripts/boards/components/modal/header.vue6
-rw-r--r--app/assets/javascripts/boards/components/modal/index.vue13
-rw-r--r--app/assets/javascripts/boards/components/modal/list.vue4
-rw-r--r--app/assets/javascripts/due_date_select.js4
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js3
-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/pages/boards.scss200
-rw-r--r--app/assets/stylesheets/performance_bar.scss8
-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/services/clusters/refresh_service.rb6
-rw-r--r--app/views/notify/links/ci/builds/_build.text.erb2
-rw-r--r--app/views/projects/issues/show.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml10
-rw-r--r--app/views/shared/boards/components/_board.html.haml13
-rw-r--r--app/views/shared/boards/components/_sidebar.html.haml6
-rw-r--r--app/views/shared/boards/components/sidebar/_due_date.html.haml2
-rw-r--r--app/views/shared/issuable/_label_page_default.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml6
-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/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/knative-0-5.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/tc-raw-log-in-plaintext-mail.yml5
-rw-r--r--changelogs/unreleased/weimeng-user-autocomplete-fix.yml5
-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/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/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/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/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.rb13
-rw-r--r--lib/gitlab/profiler.rb4
-rw-r--r--locale/gitlab.pot6
-rw-r--r--qa/qa/page/component/note.rb2
-rwxr-xr-xscripts/review_apps/review-apps.sh4
-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/wiki/user_creates_wiki_page_spec.rb4
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb16
-rw-r--r--spec/frontend/notes/components/discussion_actions_spec.js104
-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/git/commit_spec.rb37
-rw-r--r--spec/models/clusters/applications/knative_spec.rb2
-rw-r--r--spec/rack_servers/configs/puma.rb32
-rw-r--r--spec/rack_servers/puma_spec.rb20
-rw-r--r--spec/services/clusters/refresh_service_spec.rb26
-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/workers/cluster_configure_worker_spec.rb67
-rw-r--r--spec/workers/cluster_project_configure_worker_spec.rb15
92 files changed, 962 insertions, 302 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/Gemfile b/Gemfile
index c55e6478cb0..50099f5a3b1 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
diff --git a/Gemfile.lock b/Gemfile.lock
index 109958e2591..95c377e2725 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -551,10 +551,26 @@ 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)
+ 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.4.3)
optimist (3.0.0)
org-ruby (0.9.12)
@@ -865,6 +881,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 +934,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 +952,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)
@@ -1086,6 +1115,7 @@ 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)
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index 47a46502bff..667eea17d44 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -60,15 +60,11 @@ export default {
</script>
<template>
- <div class="board-blank-state p-3">
+ <div class="board-blank-state">
<p>Add the following default lists to your Issue Board with one click:</p>
- <ul class="list-unstyled board-blank-state-list">
+ <ul class="board-blank-state-list">
<li v-for="(label, index) in predefinedLabels" :key="index">
- <span
- :style="{ backgroundColor: label.color }"
- class="label-color position-relative d-inline-block rounded"
- >
- </span>
+ <span :style="{ backgroundColor: label.color }" class="label-color"> </span>
{{ label.title }}
</li>
</ul>
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index c9effa0639b..f569322ab70 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -83,7 +83,7 @@ export default {
}"
:index="index"
:data-issue-id="issue.id"
- class="board-card position-relative p-3 rounded"
+ class="board-card"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)"
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index c9972d051aa..f3f341ece5c 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -221,10 +221,7 @@ export default {
</script>
<template>
- <div
- :class="{ 'd-none': !list.isExpanded, 'd-flex flex-column': list.isExpanded }"
- class="board-list-component position-relative h-100"
- >
+ <div class="board-list-component">
<div v-if="loading" class="board-list-loading text-center" aria-label="Loading issues">
<gl-loading-icon />
</div>
@@ -239,7 +236,7 @@ export default {
:data-board="list.id"
:data-board-type="list.type"
:class="{ 'is-smaller': showIssueForm }"
- class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list"
+ class="board-list js-board-list"
>
<board-card
v-for="(issue, index) in issues"
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 70daba352f9..28d96dab605 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -95,7 +95,7 @@ export default {
<template>
<div class="board-new-issue-form">
- <div class="board-card position-relative p-3 rounded">
+ <div class="board-card">
<form @submit="submit($event)">
<div v-if="error" class="flash-container">
<div class="flash-alert">An error occurred. Please try again.</div>
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 7c03f06e650..be0de63e772 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -173,7 +173,7 @@ export default {
</script>
<template>
<div>
- <div class="d-flex board-card-header">
+ <div class="board-card-header">
<h4 class="board-card-title append-bottom-0 prepend-top-0">
<icon
v-if="issue.confidential"
@@ -214,11 +214,11 @@ export default {
</div>
<div class="board-card-footer d-flex justify-content-between align-items-end">
<div
- class="d-flex align-items-start flex-wrap-reverse board-card-number-container overflow-hidden js-board-card-number-container"
+ class="d-flex align-items-start flex-wrap-reverse board-card-number-container js-board-card-number-container"
>
<span
v-if="issue.referencePath"
- class="board-card-number overflow-hidden d-flex append-right-8 prepend-top-8"
+ class="board-card-number d-flex append-right-8 prepend-top-8"
>
<tooltip-on-truncate
v-if="issueReferencePath"
@@ -235,7 +235,7 @@ export default {
/>
</span>
</div>
- <div class="board-card-assignee d-flex">
+ <div class="board-card-assignee">
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
v-if="shouldRenderAssignee(index)"
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
index 3bc7f13a9e6..9bc66978198 100644
--- a/app/assets/javascripts/boards/components/issue_due_date.vue
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -82,11 +82,7 @@ export default {
<template>
<span>
<span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
- <icon
- :class="{ 'text-danger': isPastDue }"
- class="board-card-info-icon align-top"
- name="calendar"
- />
+ <icon :class="{ 'text-danger': isPastDue, 'board-card-info-icon': true }" name="calendar" />
<time :class="{ 'text-danger': isPastDue }" datetime="date" class="board-card-info-text">{{
body
}}</time>
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue
index 98c1d29db16..5acc3025b2c 100644
--- a/app/assets/javascripts/boards/components/issue_time_estimate.vue
+++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue
@@ -28,7 +28,7 @@ export default {
<template>
<span>
<span ref="issueTimeEstimate" class="board-card-info card-number">
- <icon name="hourglass" css-classes="board-card-info-icon align-top" /><time
+ <icon name="hourglass" css-classes="board-card-info-icon" /><time
class="board-card-info-text"
>{{ timeEstimate }}</time
>
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.vue b/app/assets/javascripts/boards/components/modal/empty_state.vue
index 091700de93f..2a0008467c4 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.vue
+++ b/app/assets/javascripts/boards/components/modal/empty_state.vue
@@ -42,8 +42,8 @@ export default {
</script>
<template>
- <section class="empty-state d-flex mt-0 h-100">
- <div class="row w-100 my-auto mx-0">
+ <section class="empty-state">
+ <div class="row">
<div class="col-12 col-md-6 order-md-last">
<aside class="svg-content d-none d-md-block"><img :src="emptyStateSvg" /></aside>
</div>
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue
index 1cfa6d39362..1f0961e02d8 100644
--- a/app/assets/javascripts/boards/components/modal/header.vue
+++ b/app/assets/javascripts/boards/components/modal/header.vue
@@ -50,8 +50,8 @@ export default {
</script>
<template>
<div>
- <header class="add-issues-header border-top-0 form-actions">
- <h2 class="m-0">
+ <header class="add-issues-header form-actions">
+ <h2>
Add issues
<button
type="button"
@@ -65,7 +65,7 @@ export default {
</h2>
</header>
<modal-tabs v-if="!loading && issuesCount > 0" />
- <div v-if="showSearch" class="d-flex append-bottom-10">
+ <div v-if="showSearch" class="add-issues-search append-bottom-10">
<modal-filters :store="filter" />
<button
ref="selectAllBtn"
diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue
index 8e09e265cfb..1e5761cf268 100644
--- a/app/assets/javascripts/boards/components/modal/index.vue
+++ b/app/assets/javascripts/boards/components/modal/index.vue
@@ -143,11 +143,8 @@ export default {
};
</script>
<template>
- <div
- v-if="showAddIssuesModal"
- class="add-issues-modal d-flex position-fixed position-top-0 position-bottom-0 position-left-0 position-right-0 h-100"
- >
- <div class="add-issues-container d-flex flex-column m-auto rounded">
+ <div v-if="showAddIssuesModal" class="add-issues-modal">
+ <div class="add-issues-container">
<modal-header
:project-id="projectId"
:milestone-path="milestonePath"
@@ -164,10 +161,8 @@ export default {
:new-issue-path="newIssuePath"
:empty-state-svg="emptyStateSvg"
/>
- <section v-if="loading || filterLoading" class="add-issues-list d-flex h-100 text-center">
- <div class="add-issues-list-loading w-100 align-self-center">
- <gl-loading-icon size="md" />
- </div>
+ <section v-if="loading || filterLoading" class="add-issues-list text-center">
+ <div class="add-issues-list-loading"><gl-loading-icon /></div>
</section>
<modal-footer />
</div>
diff --git a/app/assets/javascripts/boards/components/modal/list.vue b/app/assets/javascripts/boards/components/modal/list.vue
index 28d2019af2f..e9ed2de859d 100644
--- a/app/assets/javascripts/boards/components/modal/list.vue
+++ b/app/assets/javascripts/boards/components/modal/list.vue
@@ -117,7 +117,7 @@ export default {
};
</script>
<template>
- <section ref="list" class="add-issues-list add-issues-list-columns d-flex h-100">
+ <section ref="list" class="add-issues-list add-issues-list-columns">
<div
v-if="issuesCount > 0 && issues.length === 0"
class="empty-state add-issues-empty-state-filter text-center"
@@ -129,7 +129,7 @@ export default {
<div v-for="issue in group" v-if="showIssue(issue)" :key="issue.id" class="board-card-parent">
<div
:class="{ 'is-active': issue.selected }"
- class="board-card position-relative p-3 rounded"
+ class="board-card"
@click="toggleIssue($event, issue)"
>
<issue-card-inner :issue="issue" :issue-link-base="issueLinkBase" :root-path="rootPath" />
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/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/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/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 61fab445793..fc1c1bd9962 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -1,3 +1,7 @@
+[v-cloak] {
+ display: none;
+}
+
.user-can-drag {
cursor: grab;
}
@@ -36,15 +40,35 @@
margin: 0 8px 10px;
padding-bottom: 10px;
border-bottom: 1px solid $dropdown-divider-bg;
+
+ > p {
+ margin: 0;
+ font-size: 14px;
+ }
}
.issue-boards-page {
.content-wrapper {
padding-bottom: 0;
}
+
+ .issues-details-filters {
+ display: flex;
+ }
+
+ .filter-form {
+ width: 100%;
+ }
+}
+
+.board-extra-actions {
+ font-size: 0;
+ white-space: nowrap;
}
.boards-app {
+ position: relative;
+
@include media-breakpoint-up(sm) {
transition: width $sidebar-transition-duration;
width: 100%;
@@ -55,9 +79,17 @@
}
}
+.boards-app-loading {
+ width: 100%;
+ font-size: 34px;
+}
+
.boards-list {
height: calc(100vh - #{$issue-board-list-difference-xs});
+ width: 100%;
+ padding: $gl-padding ($gl-padding / 2);
overflow-x: scroll;
+ white-space: nowrap;
min-height: 200px;
@include media-breakpoint-only(sm) {
@@ -82,7 +114,13 @@
}
.board {
+ display: inline-block;
width: calc(85vw - 15px);
+ height: 100%;
+ padding-right: ($gl-padding / 2);
+ padding-left: ($gl-padding / 2);
+ white-space: normal;
+ vertical-align: top;
@include media-breakpoint-up(sm) {
width: 400px;
@@ -97,7 +135,23 @@
&.is-collapsed {
width: 50px;
+ .board-header {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+
+ button {
+ display: none;
+ }
+ }
+
.board-title {
+ padding: 0;
+ border-bottom: 0;
+ justify-content: center;
+
> span {
width: 100%;
margin-top: -12px;
@@ -113,16 +167,34 @@
left: 50%;
margin-left: -10px;
}
+
+ .board-list-component,
+ .issue-count-badge {
+ display: none;
+ }
+ }
+
+ &:not(.is-collapsed) {
+ .board-list-component {
+ display: flex;
+ flex-direction: column;
+ }
}
}
.board-inner {
+ position: relative;
+ height: 100%;
font-size: $issue-boards-font-size;
background: $gray-light;
border: 1px solid $border-color;
+ border-radius: $border-radius-default;
+ flex: 1;
}
.board-header {
+ position: relative;
+
&.has-border::before {
border-top: 3px solid;
border-color: inherit;
@@ -147,9 +219,18 @@
}
}
+.board-inner-container {
+ border-bottom: 1px solid $border-color;
+ padding: $gl-padding;
+}
+
.board-title {
+ margin: 0;
+ padding: $gl-padding-8 $gl-padding;
font-size: 1em;
border-bottom: 1px solid $border-color;
+ display: flex;
+ align-items: center;
}
.board-title-text {
@@ -158,8 +239,10 @@
.board-delete {
margin-right: 10px;
+ padding: 0;
color: $gray-darkest;
background-color: transparent;
+ border: 0;
outline: 0;
&:hover {
@@ -169,6 +252,7 @@
.board-blank-state,
.board-promotion-state {
+ padding: $gl-padding;
background-color: $white-light;
flex: 1;
overflow-y: auto;
@@ -176,23 +260,35 @@
}
.board-blank-state-list {
+ list-style: none;
+
> li:not(:last-child) {
margin-bottom: 8px;
}
.label-color {
+ position: relative;
top: 2px;
+ display: inline-block;
width: 16px;
height: 16px;
margin-right: 3px;
+ border-radius: $border-radius-default;
}
}
.board-list-component {
+ position: relative;
+ flex: 1;
min-height: 0; // firefox fix
}
.board-list {
+ height: 100%;
+ width: 100%;
+ margin-bottom: 0;
+ padding: $gl-padding-4;
+ list-style: none;
overflow-y: auto;
overflow-x: hidden;
}
@@ -203,9 +299,13 @@
}
.board-card {
+ position: relative;
+ padding: $gl-padding;
background: $white-light;
+ border-radius: $border-radius-default;
border: 1px solid $gray-200;
box-shadow: 0 1px 2px $issue-boards-card-shadow;
+ list-style: none;
line-height: $gl-padding;
&:not(:last-child) {
@@ -233,6 +333,10 @@
}
}
+ svg {
+ vertical-align: top;
+ }
+
.confidential-icon {
color: $orange-600;
cursor: help;
@@ -256,7 +360,12 @@
}
}
+.board-card-header {
+ display: flex;
+}
+
.board-card-assignee {
+ display: flex;
margin-top: -$gl-padding-4;
margin-bottom: -$gl-padding-4;
@@ -316,16 +425,34 @@
.board-card-number {
font-size: $gl-font-size-xs;
color: $gl-text-color-secondary;
+ overflow: hidden;
@include media-breakpoint-up(md) {
font-size: $label-font-size;
}
}
+.board-card-number-container {
+ overflow: hidden;
+}
+
+.issue-boards-search {
+ width: 395px;
+
+ .form-control {
+ display: inline-block;
+ width: 210px;
+ }
+}
+
.board-list-count {
padding: 10px 0;
color: $gl-text-color-secondary;
font-size: 13px;
+
+ > .fa {
+ margin-right: 5px;
+ }
}
.board-new-issue-form {
@@ -333,9 +460,16 @@
margin: 5px;
}
-.issue-boards-sidebar {
+.page-with-contextual-sidebar.layout-page .issue-boards-sidebar {
+ .issuable-sidebar-header {
+ position: relative;
+ }
+
.gutter-toggle {
+ position: absolute;
+ top: 0;
bottom: 15px;
+ right: 0;
width: 22px;
color: $gray-darkest;
@@ -355,6 +489,10 @@
.issuable-header-text {
@include overflow-break-word();
padding-right: 35px;
+
+ > strong {
+ font-weight: $gl-font-weight-bold;
+ }
}
}
@@ -373,25 +511,44 @@
}
.add-issues-modal {
+ display: flex;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
background-color: rgba($black, 0.3);
z-index: 9999;
}
.add-issues-container {
+ display: flex;
+ flex-direction: column;
width: 90vw;
height: 85vh;
max-width: 1100px;
min-height: 500px;
+ margin: auto;
padding: 25px 15px 0;
background-color: $white-light;
+ border-radius: $border-radius-default;
box-shadow: 0 2px 12px rgba($black, 0.5);
.empty-state {
+ display: flex;
+ flex: 1;
+ margin-top: 0;
+
&.add-issues-empty-state-filter {
flex-direction: column;
justify-content: center;
}
+ > .row {
+ width: 100%;
+ margin: auto 0;
+ }
+
.svg-content {
margin-top: -40px;
}
@@ -400,15 +557,25 @@
.add-issues-header {
margin: -25px -15px -5px;
+ border-top: 0;
border-bottom: 1px solid $border-color;
border-top-right-radius: $border-radius-default;
border-top-left-radius: $border-radius-default;
> h2 {
+ margin: 0;
font-size: 18px;
}
}
+.add-issues-search {
+ display: flex;
+
+ .issues-filters {
+ flex: 1;
+ }
+}
+
.add-issues-list-column {
width: 100%;
@@ -422,6 +589,8 @@
}
.add-issues-list {
+ display: flex;
+ flex: 1;
padding-top: 3px;
margin-left: -$gl-vert-padding;
margin-right: -$gl-vert-padding;
@@ -438,6 +607,14 @@
}
}
+.add-issues-list-loading {
+ align-self: center;
+ width: 100%;
+ padding-left: $gl-vert-padding;
+ padding-right: $gl-vert-padding;
+ font-size: 35px;
+}
+
.add-issues-footer {
margin: auto -15px 0;
padding-left: 15px;
@@ -465,6 +642,27 @@
border-radius: 50%;
}
+.modal-filters {
+ display: flex;
+
+ > .dropdown {
+ display: none;
+ margin-right: 10px;
+
+ @include media-breakpoint-up(sm) {
+ display: block;
+ }
+ }
+
+ .dropdown-menu-toggle {
+ width: 100px;
+
+ @include media-breakpoint-up(md) {
+ width: 140px;
+ }
+ }
+}
+
.board-card-info {
color: $gl-text-color-secondary;
white-space: nowrap;
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/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/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/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/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index 813fccd217b..f0d1dd162df 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -13,14 +13,14 @@
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal, show_sorting_dropdown: false
%script#js-board-promotion{ type: "text/x-template" }= render_if_exists "shared/promotions/promote_issue_board"
-#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
+#board-app.boards-app{ "v-cloak" => true, data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
.d-none.d-sm-none.d-md-block
= render 'shared/issuable/search_bar', type: :boards, board: board
- .boards-list.w-100.py-3.px-2.text-nowrap
- .boards-app-loading.w-100.text-center{ "v-if" => "loading" }
- = icon("spinner spin 2x")
- %board{ "v-cloak" => "true",
+ .boards-list
+ .boards-app-loading.text-center{ "v-if" => "loading" }
+ = icon("spinner spin")
+ %board{ "v-cloak" => true,
"v-for" => "list in state.lists",
"ref" => "board",
":list" => "list",
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index f9cfcabc015..307a0919a4c 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -1,8 +1,8 @@
-.board.d-inline-block.h-100.px-2.align-top.ws-normal{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded, "board-type-assignee": list.type === "assignee" }',
+.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded, "board-type-assignee": list.type === "assignee" }',
":data-id" => "list.id" }
- .board-inner.d-flex.flex-column.position-relative.h-100.rounded
- %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color, "position-relative": list.isExpanded, "position-absolute position-top-0 position-left-0 w-100 h-100": !list.isExpanded }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
- %h3.board-title.m-0.d-flex.align-items-center.py-2.px-3.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset), "p-0 border-bottom-0 justify-content-center": !list.isExpanded }' }
+ .board-inner.d-flex.flex-column
+ %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
+ %h3.board-title.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset) }' }
%i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }",
"aria-hidden": "true" }
@@ -31,9 +31,9 @@
%board-delete{ "inline-template" => true,
":list" => "list",
"v-if" => "!list.preset && list.id" }
- %button.board-delete.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
+ %button.board-delete.has-tooltip.float-right{ type: "button", title: _("Delete list"), "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash")
- .issue-count-badge.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", ":class": "{ 'd-none': !list.isExpanded }", "v-tooltip": true, data: { placement: "top" } }
+ .issue-count-badge.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
%span.issue-count-badge-count
%icon.mr-1{ name: "issues" }
{{ list.issuesSize }}
@@ -42,7 +42,6 @@
%button.issue-count-badge-add-button.btn.btn-sm.btn-default.ml-1.has-tooltip.js-no-trigger-collapse{ type: "button",
"@click" => "showNewIssueForm",
"v-if" => "isNewIssueShown",
- ":class": "{ 'd-none': !list.isExpanded }",
"aria-label" => _("New issue"),
"title" => _("New issue"),
data: { placement: "top", container: "body" } }
diff --git a/app/views/shared/boards/components/_sidebar.html.haml b/app/views/shared/boards/components/_sidebar.html.haml
index b4f75967a67..30e51ecc261 100644
--- a/app/views/shared/boards/components/_sidebar.html.haml
+++ b/app/views/shared/boards/components/_sidebar.html.haml
@@ -2,16 +2,16 @@
%transition{ name: "boards-sidebar-slide" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
.issuable-sidebar
- .block.issuable-sidebar-header.position-relative
+ .block.issuable-sidebar-header
%span.issuable-header-text.hide-collapsed.float-left
- %strong.bold
+ %strong
{{ issue.title }}
%br/
%span
= render_if_exists "shared/boards/components/sidebar/issue_project_path"
= precede "#" do
{{ issue.iid }}
- %a.gutter-toggle.position-absolute.position-top-0.position-right-0{ role: "button",
+ %a.gutter-toggle.float-right{ role: "button",
href: "#",
"@click.prevent" => "closeSidebar",
"aria-label" => "Toggle sidebar" }
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/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml
index a0fb5229fc3..aa4a5f0e0d3 100644
--- a/app/views/shared/issuable/_label_page_default.html.haml
+++ b/app/views/shared/issuable/_label_page_default.html.haml
@@ -11,7 +11,7 @@
= dropdown_title(title)
- if show_boards_content
.issue-board-dropdown-content
- %p.m-0
+ %p
= content_title
= dropdown_filter(filter_placeholder)
= dropdown_content
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 3d6c5d29d44..2bcfcb6fa7c 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -3,11 +3,11 @@
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
- user_can_admin_list = board && can?(current_user, :admin_list, board.parent)
-.issues-filters{ class: ("w-100" if type == :boards_modal) }
- .issues-details-filters.filtered-search-block.d-flex{ class: block_css_class, "v-pre" => type == :boards_modal }
+.issues-filters
+ .issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
- if type == :boards
= render_if_exists "shared/boards/switcher", board: board
- = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form w-100' do
+ = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
- if @can_bulk_update
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/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/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/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/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/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml
index 884868c6336..356f573c5e9 100644
--- a/config/prometheus/common_metrics.yml
+++ b/config/prometheus/common_metrics.yml
@@ -266,6 +266,6 @@
weight: 1
queries:
- 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/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/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/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/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..726c4d95da9 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -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
@@ -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/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/locale/gitlab.pot b/locale/gitlab.pot
index fc412a936e9..0d6cb4c85b5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5966,9 +5966,6 @@ msgstr ""
msgid "No details available"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No errors to display."
msgstr ""
@@ -9565,6 +9562,9 @@ msgstr ""
msgid "Toggle Sidebar"
msgstr ""
+msgid "Toggle backtrace"
+msgstr ""
+
msgid "Toggle comments for this file"
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/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 22e47ce2c0a..51cfd145ba8 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -109,7 +109,7 @@ function perform_review_app_deployment() {
ensure_namespace
install_tiller
install_external_dns
- time deploy || true
+ time deploy
wait_for_review_app_to_be_accessible
add_license
}
@@ -270,7 +270,7 @@ EOF
echoinfo "Deploying with:"
echoinfo "${HELM_CMD}"
- eval $HELM_CMD
+ eval $HELM_CMD || true
}
function wait_for_review_app_to_be_accessible() {
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/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/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/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/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/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/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/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/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/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