summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-11-08 12:06:32 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-11-08 12:06:32 +0000
commit61f0c58946ebac453b55a657cd4be1ac50a01e11 (patch)
tree7b164c1cc9dc8ab1d100ca4fe90decf6d72e984b
parentd23b2a0871f3ca507aafa949e0314625f1f0c6a7 (diff)
downloadgitlab-ce-61f0c58946ebac453b55a657cd4be1ac50a01e11.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml1
-rw-r--r--app/assets/javascripts/blob/blob_utils.js4
-rw-r--r--app/assets/javascripts/flash.js2
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js12
-rw-r--r--app/assets/javascripts/monitoring/utils.js3
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary.vue4
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/utils.js4
-rw-r--r--app/assets/javascripts/releases/list/components/release_block.vue18
-rw-r--r--app/assets/javascripts/releases/list/components/release_block_footer.vue112
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss7
-rw-r--r--app/assets/stylesheets/framework/flash.scss89
-rw-r--r--app/assets/stylesheets/framework/layout.scss3
-rw-r--r--app/controllers/clusters/clusters_controller.rb21
-rw-r--r--app/controllers/projects/releases_controller.rb1
-rw-r--r--app/helpers/users_helper.rb9
-rw-r--r--app/models/ci/pipeline.rb10
-rw-r--r--app/services/clusters/destroy_service.rb34
-rw-r--r--app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml2
-rw-r--r--app/views/layouts/_flash.html.haml2
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml5
-rw-r--r--app/views/projects/labels/index.html.haml2
-rw-r--r--changelogs/unreleased/30229-gitlab-backgroundmigration-pruneorphanedgeoevents-did-you-mean-prun.yml5
-rw-r--r--changelogs/unreleased/32962-update-gcp-credit-url.yml5
-rw-r--r--changelogs/unreleased/33121-refactor-user-counts.yml5
-rw-r--r--changelogs/unreleased/33182-fix-productivity-analytics-multiple-labels-bug.yml5
-rw-r--r--changelogs/unreleased/35637-add-start-a-trial-option-in-top-right-drop-down.yml5
-rw-r--r--changelogs/unreleased/jh_flash_messages_styling_22992.yml5
-rw-r--r--changelogs/unreleased/nfriend-move-release-data-into-footer.yml5
-rw-r--r--doc/api/license.md5
-rw-r--r--lib/gitlab/ci/config/entry/job.rb6
-rw-r--r--lib/gitlab/ci/config/entry/need.rb44
-rw-r--r--lib/gitlab/ci/config/entry/needs.rb55
-rw-r--r--lib/gitlab/ci/config/normalizer.rb20
-rw-r--r--lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml5
-rw-r--r--lib/gitlab/ci/templates/Docker.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml27
-rw-r--r--lib/gitlab/ci/templates/Julia.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Maven.gitlab-ci.yml10
-rw-r--r--lib/gitlab/ci/templates/Mono.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml12
-rw-r--r--lib/gitlab/ci/templates/Packer.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Python.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Swift.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Terraform.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/yaml_processor.rb16
-rw-r--r--lib/gitlab/config/entry/configurable.rb29
-rw-r--r--lib/gitlab/config/entry/node.rb4
-rw-r--r--lib/gitlab/config/entry/simplifiable.rb11
-rw-r--r--lib/gitlab/config/entry/validatable.rb21
-rw-r--r--locale/gitlab.pot7
-rwxr-xr-xscripts/review_apps/review-apps.sh9
-rw-r--r--spec/features/projects/labels/search_labels_spec.rb2
-rw-r--r--spec/frontend/lib/utils/datetime_utility_spec.js8
-rw-r--r--spec/frontend/releases/list/components/release_block_footer_spec.js163
-rw-r--r--spec/frontend/releases/list/components/release_block_spec.js13
-rw-r--r--spec/frontend/releases/mock_data.js2
-rw-r--r--spec/helpers/users_helper_spec.rb4
-rw-r--r--spec/javascripts/blob/viewer/index_spec.js25
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb17
-rw-r--r--spec/lib/gitlab/ci/config/entry/need_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/entry/needs_spec.rb84
-rw-r--r--spec/lib/gitlab/ci/config/normalizer_spec.rb104
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb35
-rw-r--r--spec/models/ci/pipeline_spec.rb19
-rw-r--r--spec/services/clusters/destroy_service_spec.rb56
81 files changed, 1067 insertions, 246 deletions
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 8e91d053ca0..4ed9ac03d0c 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -117,7 +117,6 @@ schedule:review-build-cng:
- source scripts/utils.sh
- install_api_client_dependencies_with_apk
- source scripts/review_apps/review-apps.sh
- - export REVIEW_APP_CONFIG_CHANGED=$(base_config_changed)
script:
- check_kube_domain
- ensure_namespace
diff --git a/app/assets/javascripts/blob/blob_utils.js b/app/assets/javascripts/blob/blob_utils.js
index 27fcc7f7b79..cc9c621c679 100644
--- a/app/assets/javascripts/blob/blob_utils.js
+++ b/app/assets/javascripts/blob/blob_utils.js
@@ -1,5 +1,5 @@
-// capture anything starting with http:// or https://
+// capture anything starting with http:// or https:// which is not already part of a html link
// up until a disallowed character or whitespace
-export const blobLinkRegex = /https?:\/\/[^"<>\\^`{|}\s]+/g;
+export const blobLinkRegex = /(?<!<a href=")https?:\/\/[^"<>\\^`{|}\s]+/g;
export default { blobLinkRegex };
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index fc9c5827ed4..2c3320b5e79 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -37,7 +37,7 @@ const createAction = config => `
`;
const createFlashEl = (message, type) => `
- <div class="flash-content flash-${type} rounded">
+ <div class="flash-${type}">
<div class="flash-text">
${_.escape(message)}
<div class="close-icon-wrapper js-close-icon">
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 118d5facafc..28143859e4c 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -541,7 +541,7 @@ export const stringifyTime = (timeObject, fullNameFormat = false) => {
* The result cannot become negative.
*
* @param endDate date string that the time difference is calculated for
- * @return {number} number of milliseconds remaining until the given date
+ * @return {Number} number of milliseconds remaining until the given date
*/
export const calculateRemainingMilliseconds = endDate => {
const remainingMilliseconds = new Date(endDate).getTime() - Date.now();
@@ -552,7 +552,7 @@ export const calculateRemainingMilliseconds = endDate => {
* Subtracts a given number of days from a given date and returns the new date.
*
* @param {Date} date the date that we will substract days from
- * @param {number} daysInPast number of days that are subtracted from a given date
+ * @param {Number} daysInPast number of days that are subtracted from a given date
* @returns {Date} Date in past as Date object
*/
export const getDateInPast = (date, daysInPast) =>
@@ -594,3 +594,11 @@ export const getDatesInRange = (d1, d2, formatter = x => x) => {
return range.map(formatter);
};
+
+/**
+ * Converts the supplied number of seconds to milliseconds.
+ *
+ * @param {Number} seconds
+ * @return {Number} number of milliseconds
+ */
+export const secondsToMilliseconds = seconds => seconds * 1000;
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
index 6747306a6d9..2ae1647011d 100644
--- a/app/assets/javascripts/monitoring/utils.js
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -1,7 +1,6 @@
import dateformat from 'dateformat';
import { secondsIn, dateTimePickerRegex, dateFormats } from './constants';
-
-const secondsToMilliseconds = seconds => seconds * 1000;
+import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
export const getTimeDiff = timeWindow => {
const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
index 5fc0e220a72..dce8b020d6f 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue
@@ -1,7 +1,7 @@
<script>
import { GlButton, GlLink, GlProgressBar } from '@gitlab/ui';
import { __ } from '~/locale';
-import { formatTime } from '~/lib/utils/datetime_utility';
+import { formatTime, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
export default {
@@ -31,7 +31,7 @@ export default {
return Math.round((this.report.success_count / this.report.total_count) * 100) || 0;
},
formattedDuration() {
- return formatTime(this.report.total_time * 1000);
+ return formatTime(secondsToMilliseconds(this.report.total_time));
},
progressBarVariant() {
if (this.successPercentage < 33) {
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/utils.js b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
index c426a5f0bb5..95466587d6b 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/utils.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
@@ -1,5 +1,5 @@
import { TestStatus } from '~/pipelines/constants';
-import { formatTime } from '~/lib/utils/datetime_utility';
+import { formatTime, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
function iconForTestStatus(status) {
switch (status) {
@@ -12,7 +12,7 @@ function iconForTestStatus(status) {
}
}
-export const formattedTime = timeInSeconds => formatTime(timeInSeconds * 1000);
+export const formattedTime = timeInSeconds => formatTime(secondsToMilliseconds(timeInSeconds));
export const addIconStatus = testCase => ({
...testCase,
diff --git a/app/assets/javascripts/releases/list/components/release_block.vue b/app/assets/javascripts/releases/list/components/release_block.vue
index 1b78901d771..2b6aa6aeff9 100644
--- a/app/assets/javascripts/releases/list/components/release_block.vue
+++ b/app/assets/javascripts/releases/list/components/release_block.vue
@@ -10,6 +10,7 @@ import { slugify } from '~/lib/utils/text_utility';
import { getLocationHash } from '~/lib/utils/url_utility';
import { scrollToElement } from '~/lib/utils/common_utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import ReleaseBlockFooter from './release_block_footer.vue';
export default {
name: 'ReleaseBlock',
@@ -19,6 +20,7 @@ export default {
GlButton,
Icon,
UserAvatarLink,
+ ReleaseBlockFooter,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -79,6 +81,9 @@ export default {
this.glFeatures.releaseEditPage && this.release._links && this.release._links.edit_url,
);
},
+ shouldShowFooter() {
+ return this.glFeatures.releaseIssueSummary;
+ },
},
mounted() {
const hash = getLocationHash();
@@ -164,7 +169,7 @@ export default {
by
<user-avatar-link
class="prepend-left-4"
- :link-href="author.path"
+ :link-href="author.web_url"
:img-src="author.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="author.username"
@@ -216,5 +221,16 @@ export default {
<div v-html="release.description_html"></div>
</div>
</div>
+
+ <release-block-footer
+ v-if="shouldShowFooter"
+ class="card-footer"
+ :commit="release.commit"
+ :commit-path="release.commit_path"
+ :tag-name="release.tag_name"
+ :tag-path="release.tag_path"
+ :author="release.author"
+ :released-at="release.released_at"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/releases/list/components/release_block_footer.vue b/app/assets/javascripts/releases/list/components/release_block_footer.vue
new file mode 100644
index 00000000000..5659f0e530b
--- /dev/null
+++ b/app/assets/javascripts/releases/list/components/release_block_footer.vue
@@ -0,0 +1,112 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import { __, sprintf } from '~/locale';
+
+export default {
+ name: 'ReleaseBlockFooter',
+ components: {
+ Icon,
+ GlLink,
+ UserAvatarLink,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ commit: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ commitPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tagName: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tagPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ author: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ releasedAt: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ releasedAtTimeAgo() {
+ return this.timeFormated(this.releasedAt);
+ },
+ userImageAltDescription() {
+ return this.author && this.author.username
+ ? sprintf(__("%{username}'s avatar"), { username: this.author.username })
+ : null;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <div v-if="commit" class="float-left mr-3 d-flex align-items-center js-commit-info">
+ <icon ref="commitIcon" name="commit" class="mr-1" />
+ <div v-gl-tooltip.bottom :title="commit.title">
+ <gl-link v-if="commitPath" :href="commitPath">
+ {{ commit.short_id }}
+ </gl-link>
+ <span v-else>{{ commit.short_id }}</span>
+ </div>
+ </div>
+
+ <div v-if="tagName" class="float-left mr-3 d-flex align-items-center js-tag-info">
+ <icon name="tag" class="mr-1" />
+ <div v-gl-tooltip.bottom :title="__('Tag')">
+ <gl-link v-if="tagPath" :href="tagPath">
+ {{ tagName }}
+ </gl-link>
+ <span v-else>{{ tagName }}</span>
+ </div>
+ </div>
+
+ <div
+ v-if="releasedAt || author"
+ class="float-left d-flex align-items-center js-author-date-info"
+ >
+ <span class="text-secondary">{{ __('Created') }}&nbsp;</span>
+ <template v-if="releasedAt">
+ <span
+ v-gl-tooltip.bottom
+ :title="tooltipTitle(releasedAt)"
+ class="text-secondary flex-shrink-0"
+ >
+ {{ releasedAtTimeAgo }}&nbsp;
+ </span>
+ </template>
+
+ <div v-if="author" class="d-flex">
+ <span class="text-secondary">{{ __('by') }}&nbsp;</span>
+ <user-avatar-link
+ :link-href="author.web_url"
+ :img-src="author.avatar_url"
+ :img-alt="userImageAltDescription"
+ :tooltip-text="author.username"
+ tooltip-placement="bottom"
+ />
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index c9b00e5ff27..885e9ac6667 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -282,8 +282,7 @@ pre code {
white-space: pre-wrap;
}
-.alert,
-.flash-notice {
+.alert {
border-radius: 0;
}
@@ -310,12 +309,10 @@ pre code {
.alert-success,
.alert-info,
.alert-warning,
-.alert-danger,
-.flash-notice {
+.alert-danger {
color: $white-light;
h4,
- a:not(.btn),
.alert-link {
color: $white-light;
}
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 8fc2fd5f53b..657e7023111 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -1,7 +1,7 @@
$notification-box-shadow-color: rgba(0, 0, 0, 0.25);
.flash-container {
- margin: 0;
+ margin-top: 10px;
margin-bottom: $gl-padding;
font-size: 14px;
position: relative;
@@ -12,17 +12,22 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
position: -webkit-sticky;
top: $flash-container-top;
z-index: 251;
+ }
- .flash-content {
- box-shadow: 0 2px 4px 0 $notification-box-shadow-color;
- }
+ &.flash-container-page {
+ margin-bottom: 0;
+ }
+
+ &:empty {
+ margin: 0;
}
.close-icon-wrapper {
- padding: ($gl-btn-padding + $gl-padding-4) $gl-padding $gl-btn-padding;
+ padding: ($gl-padding + $gl-padding-4) $gl-padding $gl-padding;
position: absolute;
right: 0;
top: 0;
+ bottom: 0;
cursor: pointer;
.close-icon {
@@ -31,13 +36,11 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
}
- .flash-notice,
.flash-alert,
+ .flash-notice,
.flash-success,
.flash-warning {
- border-radius: $border-radius-default;
- color: $white-light;
- padding-right: $gl-padding * 2;
+ padding: $gl-padding $gl-padding-32 $gl-padding ($gl-padding + $gl-padding-4);
.container-fluid,
.container-fluid.container-limited {
@@ -45,75 +48,31 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
}
- .flash-notice {
- @extend .alert;
- background-color: $blue-500;
- margin: 0;
-
- &.flash-notice-persistent {
- background-color: $blue-100;
- color: $gl-text-color;
+ .flash-alert {
+ background-color: $red-100;
+ color: $red-700;
+ }
- a {
- color: $blue-600;
+ .flash-notice {
+ background-color: $blue-100;
+ color: $blue-700;
+ }
- &:hover {
- color: $blue-800;
- text-decoration: none;
- }
- }
- }
+ .flash-success {
+ background-color: $theme-green-100;
+ color: $green-700;
}
.flash-warning {
- @extend .alert;
background-color: $orange-100;
- color: $orange-900;
+ color: $orange-800;
cursor: default;
- margin: 0;
}
.flash-text,
.flash-action {
display: inline-block;
}
-
- .flash-alert {
- @extend .alert;
- background-color: $red-500;
- margin: 0;
-
- .flash-action {
- margin-left: 5px;
- text-decoration: none;
- font-weight: $gl-font-weight-normal;
- border-bottom: 1px solid;
-
- &:hover {
- border-color: transparent;
- }
- }
- }
-
- .flash-success {
- @extend .alert;
- background-color: $green-500;
- margin: 0;
- }
-
- &.flash-container-page {
- margin-bottom: 0;
-
- .flash-notice,
- .flash-alert,
- .flash-success {
- border-radius: 0;
- }
- }
-
- &:empty {
- margin: 0;
- }
}
@include media-breakpoint-down(sm) {
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 7205324e86f..8038a367fb9 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -33,7 +33,8 @@ body {
&.limit-container-width {
.flash-container.sticky {
max-width: $limited-layout-width;
- margin: 0 auto;
+ margin-right: auto;
+ margin-left: auto;
}
}
}
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 7c5c4bb8e80..5dd4040628f 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -92,13 +92,12 @@ class Clusters::ClustersController < Clusters::BaseController
end
def destroy
- if cluster.destroy
- flash[:notice] = _('Kubernetes cluster integration was successfully removed.')
- redirect_to clusterable.index_path, status: :found
- else
- flash[:notice] = _('Kubernetes cluster integration was not removed.')
- render :show
- end
+ response = Clusters::DestroyService
+ .new(current_user, destroy_params)
+ .execute(cluster)
+
+ flash[:notice] = response[:message]
+ redirect_to clusterable.index_path, status: :found
end
def create_gcp
@@ -143,6 +142,14 @@ class Clusters::ClustersController < Clusters::BaseController
private
+ def destroy_params
+ # To be uncomented on https://gitlab.com/gitlab-org/gitlab/merge_requests/16954
+ # This MR got split into other since it was too big.
+ #
+ # params.permit(:cleanup)
+ {}
+ end
+
def update_params
if cluster.provided_by_user?
params.require(:cluster).permit(
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 40e467e9e8a..7ee5230de06 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -7,6 +7,7 @@ class Projects::ReleasesController < Projects::ApplicationController
before_action :authorize_read_release!
before_action do
push_frontend_feature_flag(:release_edit_page, project)
+ push_frontend_feature_flag(:release_issue_summary, project)
end
before_action :authorize_update_release!, only: %i[edit update]
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 4ff25d021fb..ef0cb8b4bcb 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -95,6 +95,14 @@ module UsersHelper
tabs
end
+ def trials_link_url
+ 'https://about.gitlab.com/free-trial/'
+ end
+
+ def trials_allowed?(user)
+ false
+ end
+
def get_current_user_menu_items
items = []
@@ -105,6 +113,7 @@ module UsersHelper
items << :help
items << :profile if can?(current_user, :read_user, current_user)
items << :settings if can?(current_user, :update_user, current_user)
+ items << :start_trial if trials_allowed?(current_user)
items
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 913253e4e92..12926bc2379 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -607,8 +607,14 @@ module Ci
rescue Gitlab::Ci::YamlProcessor::ValidationError => e
self.yaml_errors = e.message
nil
- rescue
- self.yaml_errors = 'Undefined error'
+ rescue => ex
+ self.yaml_errors = "Undefined error (#{Labkit::Correlation::CorrelationId.current_id})"
+
+ Gitlab::Sentry.track_acceptable_exception(ex, extra: {
+ project_id: project.id,
+ sha: sha,
+ ci_yaml_file: ci_yaml_file_path
+ })
nil
end
end
diff --git a/app/services/clusters/destroy_service.rb b/app/services/clusters/destroy_service.rb
new file mode 100644
index 00000000000..a8de04683fa
--- /dev/null
+++ b/app/services/clusters/destroy_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Clusters
+ class DestroyService
+ attr_reader :current_user, :params
+
+ def initialize(user = nil, params = {})
+ @current_user, @params = user, params.dup
+ @response = {}
+ end
+
+ def execute(cluster)
+ cleanup? ? start_cleanup!(cluster) : destroy_cluster!(cluster)
+
+ @response
+ end
+
+ private
+
+ def cleanup?
+ Gitlab::Utils.to_boolean(params[:cleanup])
+ end
+
+ def start_cleanup!(cluster)
+ cluster.start_cleanup!
+ @response[:message] = _('Kubernetes cluster integration and resources are being removed.')
+ end
+
+ def destroy_cluster!(cluster)
+ cluster.destroy!
+ @response[:message] = _('Kubernetes cluster integration was successfully removed.')
+ end
+ end
+end
diff --git a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
index a9299af8d78..617e5d1d5d3 100644
--- a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
@@ -7,6 +7,6 @@
.gcp-signup-offer--copy
%h4= s_('ClusterIntegration|Did you know?')
%p= s_('ClusterIntegration|Every new Google Cloud Platform (GCP) account receives $300 in credit upon %{sign_up_link}. In partnership with Google, GitLab is able to offer an additional $200 for both new and existing GCP accounts to get started with GitLab\'s Google Kubernetes Engine Integration.').html_safe % { sign_up_link: link }
- %a.btn.btn-default{ href: 'https://goo.gl/AaJzRW', target: '_blank', rel: 'noopener noreferrer' }
+ %a.btn.btn-default{ href: 'https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form', target: '_blank', rel: 'noopener noreferrer' }
= s_("ClusterIntegration|Apply for credit")
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index 92572f0308c..a0815f3a565 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -3,7 +3,7 @@
- flash.each do |key, value|
-# Don't show a flash message if the message is nil
- if value
- %div{ class: "flash-content flash-#{key} rounded" }
+ %div{ class: "flash-#{key}" }
%span= value
%div{ class: "close-icon-wrapper js-close-icon" }
= sprite_icon('close', size: 16, css_class: 'close-icon')
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 484a5053a4b..8fb335c3801 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -18,6 +18,11 @@
- if current_user_menu?(:profile)
%li
= link_to s_("CurrentUser|Profile"), current_user, class: 'profile-link', data: { user: current_user.username }
+ - if current_user_menu?(:start_trial)
+ %li
+ %a.profile-link{ href: trials_link_url }
+ = s_("CurrentUser|Start a trial")
+ = emoji_icon('rocket')
- if current_user_menu?(:settings)
%li
= link_to s_("CurrentUser|Settings"), profile_path, data: { qa_selector: 'settings_link' }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 0328751c68c..0373e37818d 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -26,7 +26,7 @@
= render partial: 'shared/label', collection: @prioritized_labels, as: :label, locals: { force_priority: true, subject: @project }
- elsif search.present?
.nothing-here-block
- = _('No prioritised labels with such name or description')
+ = _('No prioritized labels with such name or description')
- if @labels.present?
.other-labels
diff --git a/changelogs/unreleased/30229-gitlab-backgroundmigration-pruneorphanedgeoevents-did-you-mean-prun.yml b/changelogs/unreleased/30229-gitlab-backgroundmigration-pruneorphanedgeoevents-did-you-mean-prun.yml
new file mode 100644
index 00000000000..daaf051ea0c
--- /dev/null
+++ b/changelogs/unreleased/30229-gitlab-backgroundmigration-pruneorphanedgeoevents-did-you-mean-prun.yml
@@ -0,0 +1,5 @@
+---
+title: "[Geo] Fix: undefined Gitlab::BackgroundMigration::PruneOrphanedGeoEvents"
+merge_request: 19638
+author:
+type: fixed
diff --git a/changelogs/unreleased/32962-update-gcp-credit-url.yml b/changelogs/unreleased/32962-update-gcp-credit-url.yml
new file mode 100644
index 00000000000..87e3e7ff364
--- /dev/null
+++ b/changelogs/unreleased/32962-update-gcp-credit-url.yml
@@ -0,0 +1,5 @@
+---
+title: Update GCP credit URLs
+merge_request: 19683
+author:
+type: fixed
diff --git a/changelogs/unreleased/33121-refactor-user-counts.yml b/changelogs/unreleased/33121-refactor-user-counts.yml
new file mode 100644
index 00000000000..6e3ee5f18f3
--- /dev/null
+++ b/changelogs/unreleased/33121-refactor-user-counts.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor maximum user counts in license
+merge_request: 19071
+author: briankabiro
+type: changed
diff --git a/changelogs/unreleased/33182-fix-productivity-analytics-multiple-labels-bug.yml b/changelogs/unreleased/33182-fix-productivity-analytics-multiple-labels-bug.yml
new file mode 100644
index 00000000000..4c306c539d4
--- /dev/null
+++ b/changelogs/unreleased/33182-fix-productivity-analytics-multiple-labels-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Fix productivity analytics listing with multiple labels
+merge_request: 33182
+author:
+type: fixed
diff --git a/changelogs/unreleased/35637-add-start-a-trial-option-in-top-right-drop-down.yml b/changelogs/unreleased/35637-add-start-a-trial-option-in-top-right-drop-down.yml
new file mode 100644
index 00000000000..f07de3cc104
--- /dev/null
+++ b/changelogs/unreleased/35637-add-start-a-trial-option-in-top-right-drop-down.yml
@@ -0,0 +1,5 @@
+---
+title: Add start a trial option in the top-right user dropdown
+merge_request: 19632
+author:
+type: added
diff --git a/changelogs/unreleased/jh_flash_messages_styling_22992.yml b/changelogs/unreleased/jh_flash_messages_styling_22992.yml
new file mode 100644
index 00000000000..efbc2e3cd1a
--- /dev/null
+++ b/changelogs/unreleased/jh_flash_messages_styling_22992.yml
@@ -0,0 +1,5 @@
+---
+title: Update flash messages color sitewide
+merge_request: 18369
+author:
+type: changed
diff --git a/changelogs/unreleased/nfriend-move-release-data-into-footer.yml b/changelogs/unreleased/nfriend-move-release-data-into-footer.yml
new file mode 100644
index 00000000000..64772924d26
--- /dev/null
+++ b/changelogs/unreleased/nfriend-move-release-data-into-footer.yml
@@ -0,0 +1,5 @@
+---
+title: Move release meta-data into footer on Releases page
+merge_request: 19451
+author:
+type: changed
diff --git a/doc/api/license.md b/doc/api/license.md
index 12f1d03d576..c56a5fee95a 100644
--- a/doc/api/license.md
+++ b/doc/api/license.md
@@ -17,6 +17,7 @@ GET /license
"starts_at": "2018-01-27",
"expires_at": "2022-01-27",
"historical_max": 300,
+ "maximum_user_count": 300,
"expired": false,
"overage": 200,
"user_limit": 100,
@@ -46,6 +47,7 @@ GET /licenses
"starts_at": "2018-01-27",
"expires_at": "2022-01-27",
"historical_max": 300,
+ "maximum_user_count": 300,
"expired": false,
"overage": 200,
"user_limit": 100,
@@ -64,6 +66,7 @@ GET /licenses
"starts_at": "2018-01-27",
"expires_at": "2022-01-27",
"historical_max": 300,
+ "maximum_user_count": 300,
"expired": false,
"overage": 200,
"user_limit": 100,
@@ -112,6 +115,7 @@ Example response:
"starts_at": "2018-01-27",
"expires_at": "2022-01-27",
"historical_max": 300,
+ "maximum_user_count": 300,
"expired": false,
"overage": 200,
"user_limit": 100,
@@ -155,6 +159,7 @@ Example response:
"starts_at": "2018-01-27",
"expires_at": "2022-01-27",
"historical_max": 300,
+ "maximum_user_count": 300,
"expired": false,
"overage": 200,
"user_limit": 100,
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 1298e2d3462..2d5981a4255 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -50,7 +50,6 @@ module Gitlab
validates :timeout, duration: { limit: ChronicDuration.output(Project::MAX_BUILD_TIMEOUT) }
validates :dependencies, array_of_strings: true
- validates :needs, array_of_strings: true
validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true
end
@@ -114,6 +113,11 @@ module Gitlab
description: 'List of evaluable Rules to determine job inclusion.',
inherit: false
+ entry :needs, Entry::Needs,
+ description: 'Needs configuration for this job.',
+ metadata: { allowed_needs: %i[job] },
+ inherit: false
+
entry :variables, Entry::Variables,
description: 'Environment variables available for this job.',
inherit: false
diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb
new file mode 100644
index 00000000000..b6db546d8ff
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/need.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ class Need < ::Gitlab::Config::Entry::Simplifiable
+ strategy :Job, if: -> (config) { config.is_a?(String) }
+
+ class Job < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, presence: true
+ validates :config, type: String
+ end
+
+ def type
+ :job
+ end
+
+ def value
+ { name: @config }
+ end
+ end
+
+ class UnknownStrategy < ::Gitlab::Config::Entry::Node
+ def type
+ end
+
+ def value
+ end
+
+ def errors
+ ["#{location} has an unsupported type"]
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+::Gitlab::Ci::Config::Entry::Need.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need')
diff --git a/lib/gitlab/ci/config/entry/needs.rb b/lib/gitlab/ci/config/entry/needs.rb
new file mode 100644
index 00000000000..28452aaaa16
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/needs.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a set of needs dependencies.
+ #
+ class Needs < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, presence: true
+
+ validate do
+ unless config.is_a?(Hash) || config.is_a?(Array)
+ errors.add(:config, 'can only be a Hash or an Array')
+ end
+ end
+
+ validate on: :composed do
+ extra_keys = value.keys - opt(:allowed_needs)
+ if extra_keys.any?
+ errors.add(:config, "uses invalid types: #{extra_keys.join(', ')}")
+ end
+ end
+ end
+
+ def compose!(deps = nil)
+ super(deps) do
+ [@config].flatten.each_with_index do |need, index|
+ @entries[index] = ::Gitlab::Config::Entry::Factory.new(Entry::Need)
+ .value(need)
+ .with(key: "need", parent: self, description: "need definition.") # rubocop:disable CodeReuse/ActiveRecord
+ .create!
+ end
+
+ @entries.each_value do |entry|
+ entry.compose!(deps)
+ end
+ end
+ end
+
+ def value
+ values = @entries.values.select(&:type)
+ values.group_by(&:type).transform_values do |values|
+ values.map(&:value)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb
index 09f9bf5f69f..e714ef225f5 100644
--- a/lib/gitlab/ci/config/normalizer.rb
+++ b/lib/gitlab/ci/config/normalizer.rb
@@ -18,8 +18,8 @@ module Gitlab
config[:dependencies] = expand_names(config[:dependencies])
end
- if config[:needs]
- config[:needs] = expand_names(config[:needs])
+ if job_needs = config.dig(:needs, :job)
+ config[:needs][:job] = expand_needs(job_needs)
end
config
@@ -36,6 +36,22 @@ module Gitlab
end
end
+ def expand_needs(job_needs)
+ return unless job_needs
+
+ job_needs.flat_map do |job_need|
+ job_need_name = job_need[:name].to_sym
+
+ if all_job_names = parallelized_jobs[job_need_name]
+ all_job_names.map do |job_name|
+ { name: job_name }
+ end
+ else
+ job_need
+ end
+ end
+ end
+
def parallelized_jobs
strong_memoize(:parallelized_jobs) do
@jobs_config.each_with_object({}) do |(job_name, config), hash|
diff --git a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
index be584814271..4ec3bb15230 100644
--- a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
@@ -113,9 +113,10 @@ promoteBeta:
promoteProduction:
extends: .promote_job
stage: production
- # We only allow production promotion on `master` because
+ # We only allow production promotion on the default branch because
# it has its own production scoped secret variables
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
script:
- bundle exec fastlane promote_beta_to_production
diff --git a/lib/gitlab/ci/templates/Docker.gitlab-ci.yml b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
index 15cdbf63cb1..5160c05a251 100644
--- a/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
@@ -10,7 +10,8 @@ docker-build-master:
- docker build --pull -t "$CI_REGISTRY_IMAGE" .
- docker push "$CI_REGISTRY_IMAGE"
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
docker-build:
# Official docker image.
@@ -24,4 +25,5 @@ docker-build:
- docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index a95714d5684..416aa19e666 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -24,9 +24,8 @@ review:
- tags
kubernetes: active
except:
- refs:
- - master
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $REVIEW_DISABLED
stop_review:
@@ -48,9 +47,8 @@ stop_review:
- tags
kubernetes: active
except:
- refs:
- - master
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $REVIEW_DISABLED
# Staging deploys are disabled by default since
@@ -73,10 +71,9 @@ staging:
name: staging
url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_INGRESS_BASE_DOMAIN
only:
- refs:
- - master
kubernetes: active
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $STAGING_ENABLED
# Canaries are disabled by default, but if you want them,
@@ -98,10 +95,9 @@ canary:
url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
when: manual
only:
- refs:
- - master
kubernetes: active
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $CANARY_ENABLED
.production: &production_template
@@ -126,9 +122,9 @@ canary:
production:
<<: *production_template
only:
- refs:
- - master
kubernetes: active
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
except:
variables:
- $STAGING_ENABLED
@@ -141,10 +137,9 @@ production_manual:
when: manual
allow_failure: false
only:
- refs:
- - master
kubernetes: active
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $STAGING_ENABLED
- $CANARY_ENABLED
except:
@@ -152,7 +147,7 @@ production_manual:
- $INCREMENTAL_ROLLOUT_ENABLED
- $INCREMENTAL_ROLLOUT_MODE
-# This job implements incremental rollout on for every push to `master`.
+# This job implements incremental rollout for every push to the default branch.
.rollout: &rollout_template
extends: .auto-deploy
@@ -178,10 +173,9 @@ production_manual:
when: manual
# This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4)
only:
- refs:
- - master
kubernetes: active
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $INCREMENTAL_ROLLOUT_MODE == "manual"
- $INCREMENTAL_ROLLOUT_ENABLED
except:
@@ -193,10 +187,9 @@ production_manual:
when: delayed
start_in: 5 minutes
only:
- refs:
- - master
kubernetes: active
variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- $INCREMENTAL_ROLLOUT_MODE == "timed"
timed rollout 10%:
diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
index 32d4e07d398..56785f2017d 100644
--- a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
@@ -64,7 +64,8 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
# WARNING: This template is using the `julia` images from [Docker
# Hub][3]. One can use custom Julia images and/or the official ones found
diff --git a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
index 84bb0ff3b33..f6b69051bfa 100644
--- a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
@@ -6,7 +6,7 @@
# This template will build and test your projects
# * Caches downloaded dependencies and plugins between invocation.
# * Verify but don't deploy merge requests.
-# * Deploy built artifacts from master branch only.
+# * Deploy built artifacts from the default branch only.
variables:
# This will suppress any download for dependencies and plugins or upload messages which would clutter the console log.
@@ -33,7 +33,8 @@ cache:
script:
- 'mvn $MAVEN_CLI_OPTS verify'
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
# Verify merge requests using JDK8
verify:jdk8:
@@ -42,7 +43,7 @@ verify:jdk8:
# To deploy packages from CI, create a ci_settings.xml file
# For deploying packages to GitLab's Maven Repository: See https://docs.gitlab.com/ee/user/project/packages/maven_repository.html#creating-maven-packages-with-gitlab-cicd for more details.
# Please note: The GitLab Maven Repository is currently only available in GitLab Premium / Ultimate.
-# For `master` branch run `mvn deploy` automatically.
+# For the default branch run `mvn deploy` automatically.
deploy:jdk8:
stage: deploy
script:
@@ -51,4 +52,5 @@ deploy:jdk8:
fi
- 'mvn $MAVEN_CLI_OPTS deploy -s ci_settings.xml'
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
index 10fb6be6c39..9192f233eac 100644
--- a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
@@ -25,7 +25,8 @@ before_script:
release:
stage: deploy
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
artifacts:
paths:
- build/release/MyProject.exe
diff --git a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
index 65abee1f5eb..91de258eb7c 100644
--- a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
@@ -49,7 +49,8 @@ review:
only:
- branches
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
stop-review:
<<: *deploy
@@ -66,7 +67,8 @@ stop-review:
only:
- branches
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
staging:
<<: *deploy
@@ -78,7 +80,8 @@ staging:
name: staging
url: http://$CI_PROJECT_NAME-staging.$OPENSHIFT_DOMAIN
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
production:
<<: *deploy
@@ -91,4 +94,5 @@ production:
name: production
url: http://$CI_PROJECT_NAME.$OPENSHIFT_DOMAIN
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Packer.gitlab-ci.yml b/lib/gitlab/ci/templates/Packer.gitlab-ci.yml
index 0a3cf3dcf77..28255eb893c 100644
--- a/lib/gitlab/ci/templates/Packer.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Packer.gitlab-ci.yml
@@ -25,4 +25,5 @@ build:
- find . -maxdepth 1 -name '*.json' -print0 | xargs -t0n1 packer build
when: manual
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
index d2dd3fbfb75..18778326029 100644
--- a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
@@ -12,4 +12,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml
index ba422c08614..920b2c7dbd0 100644
--- a/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml
@@ -10,4 +10,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
index a683561a455..87f70abe0be 100644
--- a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
@@ -14,4 +14,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
index 92f25280c6e..8aee121a2e9 100644
--- a/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml
@@ -9,4 +9,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
index 0e206423fa5..a784e89a6ca 100644
--- a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
@@ -12,4 +12,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml
index d91a8d7421f..0750bb2cd97 100644
--- a/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml
@@ -14,4 +14,5 @@ pages:
- node_modules
key: project
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
index 9a3ecd1c34f..45b06c040bd 100644
--- a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
@@ -8,10 +8,12 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
test:
script:
- hugo
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml
index 7a441a2f70f..6fadda88a30 100644
--- a/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml
@@ -11,7 +11,8 @@ test:
- pip install hyde
- hyde gen
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
pages:
stage: deploy
@@ -22,4 +23,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml
index e7dacd3a1fc..ef0adfdfcf2 100644
--- a/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml
@@ -17,7 +17,8 @@ test:
paths:
- test
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
pages:
stage: deploy
@@ -27,4 +28,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
index 2d26b86a328..b53fcb3308a 100644
--- a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
@@ -34,4 +34,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml
index 93ab8e0be0d..7fec535aedd 100644
--- a/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml
@@ -9,4 +9,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
index 6524405133a..7e661fc9858 100644
--- a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
@@ -13,4 +13,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml
index 57ac323dfdf..cdd50485a81 100644
--- a/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml
@@ -12,7 +12,8 @@ test:
- bundle install --path vendor
- bundle exec middleman build
except:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
pages:
script:
@@ -24,4 +25,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml
index 7f037b5f5cf..be1a2d0ff0a 100644
--- a/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml
@@ -9,4 +9,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml
index 6d912a89bc1..616f9a6c99b 100644
--- a/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml
@@ -12,4 +12,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
index 8fd08ea7995..f3af9db3b42 100644
--- a/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
@@ -26,4 +26,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
index 00b8b94b574..9f115c05802 100644
--- a/lib/gitlab/ci/templates/Python.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
@@ -48,4 +48,5 @@ pages:
paths:
- public
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml
index ffed7a0fec2..f7b4552f8da 100644
--- a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml
@@ -22,7 +22,8 @@ archive_project:
- xcodebuild clean archive -archivePath build/ProjectName -scheme SchemeName
- xcodebuild -exportArchive -exportFormat ipa -archivePath "build/ProjectName.xcarchive" -exportPath "build/ProjectName.ipa" -exportProvisioningProfile "ProvisioningProfileName"
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
artifacts:
paths:
- build/ProjectName.ipa
diff --git a/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
index f374bc7e26a..85c5104eaad 100644
--- a/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
@@ -53,4 +53,5 @@ apply:
- plan
when: manual
only:
- - master
+ variables:
+ - $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index f6a3abefcfb..c2a55fa8b1b 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -40,7 +40,7 @@ module Gitlab
environment: job[:environment_name],
coverage_regex: job[:coverage],
yaml_variables: yaml_variables(name),
- needs_attributes: job[:needs]&.map { |need| { name: need } },
+ needs_attributes: job.dig(:needs, :job),
interruptible: job[:interruptible],
rules: job[:rules],
options: {
@@ -59,7 +59,7 @@ module Gitlab
instance: job[:instance],
start_in: job[:start_in],
trigger: job[:trigger],
- bridge_needs: job[:needs]
+ bridge_needs: job.dig(:needs, :bridge)&.first
}.compact }.compact
end
@@ -159,17 +159,19 @@ module Gitlab
end
def validate_job_needs!(name, job)
- return unless job[:needs]
+ return unless job.dig(:needs, :job)
stage_index = @stages.index(job[:stage])
- job[:needs].each do |need|
- raise ValidationError, "#{name} job: undefined need: #{need}" unless @jobs[need.to_sym]
+ job.dig(:needs, :job).each do |need|
+ need_job_name = need[:name]
- needs_stage_index = @stages.index(@jobs[need.to_sym][:stage])
+ raise ValidationError, "#{name} job: undefined need: #{need_job_name}" unless @jobs[need_job_name.to_sym]
+
+ needs_stage_index = @stages.index(@jobs[need_job_name.to_sym][:stage])
unless needs_stage_index.present? && needs_stage_index < stage_index
- raise ValidationError, "#{name} job: need #{need} is not defined in prior stages"
+ raise ValidationError, "#{name} job: need #{need_job_name} is not defined in prior stages"
end
end
end
diff --git a/lib/gitlab/config/entry/configurable.rb b/lib/gitlab/config/entry/configurable.rb
index b7ec4b7c4f8..bda84dc2cff 100644
--- a/lib/gitlab/config/entry/configurable.rb
+++ b/lib/gitlab/config/entry/configurable.rb
@@ -29,22 +29,24 @@ module Gitlab
def compose!(deps = nil)
return unless valid?
- self.class.nodes.each do |key, factory|
- # If we override the config type validation
- # we can end with different config types like String
- next unless config.is_a?(Hash)
+ super do
+ self.class.nodes.each do |key, factory|
+ # If we override the config type validation
+ # we can end with different config types like String
+ next unless config.is_a?(Hash)
- factory
- .value(config[key])
- .with(key: key, parent: self)
+ factory
+ .value(config[key])
+ .with(key: key, parent: self)
- entries[key] = factory.create!
- end
+ entries[key] = factory.create!
+ end
- yield if block_given?
+ yield if block_given?
- entries.each_value do |entry|
- entry.compose!(deps)
+ entries.each_value do |entry|
+ entry.compose!(deps)
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -67,12 +69,13 @@ module Gitlab
private
# rubocop: disable CodeReuse/ActiveRecord
- def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil)
+ def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil, metadata: {})
factory = ::Gitlab::Config::Entry::Factory.new(entry)
.with(description: description)
.with(default: default)
.with(inherit: inherit)
.with(reserved: reserved)
+ .metadata(metadata)
(@nodes ||= {}).merge!(key.to_sym => factory)
end
diff --git a/lib/gitlab/config/entry/node.rb b/lib/gitlab/config/entry/node.rb
index e014f15fbd8..84d3409ed91 100644
--- a/lib/gitlab/config/entry/node.rb
+++ b/lib/gitlab/config/entry/node.rb
@@ -112,6 +112,10 @@ module Gitlab
@aspects ||= []
end
+ def self.with_aspect(blk)
+ self.aspects.append(blk)
+ end
+
private
attr_reader :entries
diff --git a/lib/gitlab/config/entry/simplifiable.rb b/lib/gitlab/config/entry/simplifiable.rb
index d58aba07d15..315f1947e2c 100644
--- a/lib/gitlab/config/entry/simplifiable.rb
+++ b/lib/gitlab/config/entry/simplifiable.rb
@@ -4,11 +4,11 @@ module Gitlab
module Config
module Entry
class Simplifiable < SimpleDelegator
- EntryStrategy = Struct.new(:name, :condition)
+ EntryStrategy = Struct.new(:name, :klass, :condition)
attr_reader :subject
- def initialize(config, **metadata)
+ def initialize(config, **metadata, &blk)
unless self.class.const_defined?(:UnknownStrategy)
raise ArgumentError, 'UndefinedStrategy not available!'
end
@@ -19,14 +19,13 @@ module Gitlab
entry = self.class.entry_class(strategy)
- @subject = entry.new(config, metadata)
+ @subject = entry.new(config, metadata, &blk)
- yield(@subject) if block_given?
super(@subject)
end
def self.strategy(name, **opts)
- EntryStrategy.new(name, opts.fetch(:if)).tap do |strategy|
+ EntryStrategy.new(name, opts.dig(:class), opts.fetch(:if)).tap do |strategy|
strategies.append(strategy)
end
end
@@ -37,7 +36,7 @@ module Gitlab
def self.entry_class(strategy)
if strategy.present?
- self.const_get(strategy.name, false)
+ strategy.klass || self.const_get(strategy.name, false)
else
self::UnknownStrategy
end
diff --git a/lib/gitlab/config/entry/validatable.rb b/lib/gitlab/config/entry/validatable.rb
index 1c88c68c11c..45b852dc2e0 100644
--- a/lib/gitlab/config/entry/validatable.rb
+++ b/lib/gitlab/config/entry/validatable.rb
@@ -7,14 +7,27 @@ module Gitlab
extend ActiveSupport::Concern
def self.included(node)
- node.aspects.append -> do
- @validator = self.class.validator.new(self)
- @validator.validate(:new)
+ node.with_aspect -> do
+ validate(:new)
end
end
+ def validator
+ @validator ||= self.class.validator.new(self)
+ end
+
+ def validate(context = nil)
+ validator.validate(context)
+ end
+
+ def compose!(deps = nil, &blk)
+ super(deps, &blk)
+
+ validate(:composed)
+ end
+
def errors
- @validator.messages + descendants.flat_map(&:errors) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ validator.messages + descendants.flat_map(&:errors)
end
class_methods do
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index db409df295b..bcf7405a711 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -4958,6 +4958,9 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
+msgid "CurrentUser|Start a trial"
+msgstr ""
+
msgid "Custom CI configuration path"
msgstr ""
@@ -9676,7 +9679,7 @@ msgstr ""
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr ""
-msgid "Kubernetes cluster integration was not removed."
+msgid "Kubernetes cluster integration and resources are being removed."
msgstr ""
msgid "Kubernetes cluster integration was successfully removed."
@@ -11285,7 +11288,7 @@ msgstr ""
msgid "No preview for this file type"
msgstr ""
-msgid "No prioritised labels with such name or description"
+msgid "No prioritized labels with such name or description"
msgstr ""
msgid "No public groups"
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index b405e992b63..0842f7871ee 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -207,19 +207,16 @@ function download_chart() {
}
function base_config_changed() {
- git fetch origin master --depth=50
+ if [ -z "${CI_MERGE_REQUEST_IID}" ]; then return; fi
- [ -n "$(git diff origin/master... --name-only -- scripts/review_apps/base-config.yaml)" ]
+ curl "${CI_API_V4_URL}/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/changes" | jq '.changes | any(.old_path == "scripts/review_apps/base-config.yaml")'
}
function deploy() {
local name="$CI_ENVIRONMENT_SLUG"
local edition="${GITLAB_EDITION-ce}"
local base_config_file_ref="master"
- echo "REVIEW_APP_CONFIG_CHANGED: ${REVIEW_APP_CONFIG_CHANGED}"
- if [ -n "${REVIEW_APP_CONFIG_CHANGED}" ]; then
- base_config_file_ref="$CI_COMMIT_SHA"
- fi
+ if [[ "$(base_config_changed)" == "true" ]]; then base_config_file_ref="$CI_COMMIT_SHA"; fi
local base_config_file="https://gitlab.com/gitlab-org/gitlab/raw/${base_config_file_ref}/scripts/review_apps/base-config.yaml"
echoinfo "Deploying ${name}..." true
diff --git a/spec/features/projects/labels/search_labels_spec.rb b/spec/features/projects/labels/search_labels_spec.rb
index 2d5a138c3cc..e2eec7400ff 100644
--- a/spec/features/projects/labels/search_labels_spec.rb
+++ b/spec/features/projects/labels/search_labels_spec.rb
@@ -68,7 +68,7 @@ describe 'Search for labels', :js do
find('#label-search').native.send_keys(:enter)
page.within('.prioritized-labels') do
- expect(page).to have_content('No prioritised labels with such name or description')
+ expect(page).to have_content('No prioritized labels with such name or description')
end
page.within('.other-labels') do
diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js
index e4c97543b03..ee27789b6b9 100644
--- a/spec/frontend/lib/utils/datetime_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime_utility_spec.js
@@ -474,3 +474,11 @@ describe('getDatesInRange', () => {
});
});
});
+
+describe('secondsToMilliseconds', () => {
+ it('converts seconds to milliseconds correctly', () => {
+ expect(datetimeUtility.secondsToMilliseconds(0)).toBe(0);
+ expect(datetimeUtility.secondsToMilliseconds(60)).toBe(60000);
+ expect(datetimeUtility.secondsToMilliseconds(123)).toBe(123000);
+ });
+});
diff --git a/spec/frontend/releases/list/components/release_block_footer_spec.js b/spec/frontend/releases/list/components/release_block_footer_spec.js
new file mode 100644
index 00000000000..172147f1cc8
--- /dev/null
+++ b/spec/frontend/releases/list/components/release_block_footer_spec.js
@@ -0,0 +1,163 @@
+import { mount } from '@vue/test-utils';
+import ReleaseBlockFooter from '~/releases/list/components/release_block_footer.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlLink } from '@gitlab/ui';
+import { trimText } from 'helpers/text_helper';
+import { release } from '../../mock_data';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+
+jest.mock('~/vue_shared/mixins/timeago', () => ({
+ methods: {
+ timeFormated() {
+ return '7 fortnightes ago';
+ },
+ tooltipTitle() {
+ return 'February 30, 2401';
+ },
+ },
+}));
+
+describe('Release block footer', () => {
+ let wrapper;
+ let releaseClone;
+
+ const factory = (props = {}) => {
+ wrapper = mount(ReleaseBlockFooter, {
+ propsData: {
+ ...convertObjectPropsToCamelCase(releaseClone),
+ ...props,
+ },
+ sync: false,
+ });
+
+ return wrapper.vm.$nextTick();
+ };
+
+ beforeEach(() => {
+ releaseClone = JSON.parse(JSON.stringify(release));
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const commitInfoSection = () => wrapper.find('.js-commit-info');
+ const commitInfoSectionLink = () => commitInfoSection().find(GlLink);
+ const tagInfoSection = () => wrapper.find('.js-tag-info');
+ const tagInfoSectionLink = () => tagInfoSection().find(GlLink);
+ const authorDateInfoSection = () => wrapper.find('.js-author-date-info');
+
+ describe('with all props provided', () => {
+ beforeEach(() => factory());
+
+ it('renders the commit icon', () => {
+ const commitIcon = commitInfoSection().find(Icon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('commit');
+ });
+
+ it('renders the commit SHA with a link', () => {
+ const commitLink = commitInfoSectionLink();
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(releaseClone.commit.short_id);
+ expect(commitLink.attributes('href')).toBe(releaseClone.commit_path);
+ });
+
+ it('renders the tag icon', () => {
+ const commitIcon = tagInfoSection().find(Icon);
+
+ expect(commitIcon.exists()).toBe(true);
+ expect(commitIcon.props('name')).toBe('tag');
+ });
+
+ it('renders the tag name with a link', () => {
+ const commitLink = tagInfoSection().find(GlLink);
+
+ expect(commitLink.exists()).toBe(true);
+ expect(commitLink.text()).toBe(releaseClone.tag_name);
+ expect(commitLink.attributes('href')).toBe(releaseClone.tag_path);
+ });
+
+ it('renders the author and creation time info', () => {
+ expect(trimText(authorDateInfoSection().text())).toBe(
+ `Created 7 fortnightes ago by ${releaseClone.author.username}`,
+ );
+ });
+
+ it("renders the author's avatar image", () => {
+ const avatarImg = authorDateInfoSection().find('img');
+
+ expect(avatarImg.exists()).toBe(true);
+ expect(avatarImg.attributes('src')).toBe(releaseClone.author.avatar_url);
+ });
+
+ it("renders a link to the author's profile", () => {
+ const authorLink = authorDateInfoSection().find(GlLink);
+
+ expect(authorLink.exists()).toBe(true);
+ expect(authorLink.attributes('href')).toBe(releaseClone.author.web_url);
+ });
+ });
+
+ describe('without any commit info', () => {
+ beforeEach(() => factory({ commit: undefined }));
+
+ it('does not render any commit info', () => {
+ expect(commitInfoSection().exists()).toBe(false);
+ });
+ });
+
+ describe('without a commit URL', () => {
+ beforeEach(() => factory({ commitPath: undefined }));
+
+ it('renders the commit SHA as plain text (instead of a link)', () => {
+ expect(commitInfoSectionLink().exists()).toBe(false);
+ expect(commitInfoSection().text()).toBe(releaseClone.commit.short_id);
+ });
+ });
+
+ describe('without a tag name', () => {
+ beforeEach(() => factory({ tagName: undefined }));
+
+ it('does not render any tag info', () => {
+ expect(tagInfoSection().exists()).toBe(false);
+ });
+ });
+
+ describe('without a tag URL', () => {
+ beforeEach(() => factory({ tagPath: undefined }));
+
+ it('renders the tag name as plain text (instead of a link)', () => {
+ expect(tagInfoSectionLink().exists()).toBe(false);
+ expect(tagInfoSection().text()).toBe(releaseClone.tag_name);
+ });
+ });
+
+ describe('without any author info', () => {
+ beforeEach(() => factory({ author: undefined }));
+
+ it('renders the release date without the author name', () => {
+ expect(trimText(authorDateInfoSection().text())).toBe('Created 7 fortnightes ago');
+ });
+ });
+
+ describe('without a released at date', () => {
+ beforeEach(() => factory({ releasedAt: undefined }));
+
+ it('renders the author name without the release date', () => {
+ expect(trimText(authorDateInfoSection().text())).toBe(
+ `Created by ${releaseClone.author.username}`,
+ );
+ });
+ });
+
+ describe('without a release date or author info', () => {
+ beforeEach(() => factory({ author: undefined, releasedAt: undefined }));
+
+ it('does not render any author or release date info', () => {
+ expect(authorDateInfoSection().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/releases/list/components/release_block_spec.js b/spec/frontend/releases/list/components/release_block_spec.js
index 6601c4265f6..b63ef068d8e 100644
--- a/spec/frontend/releases/list/components/release_block_spec.js
+++ b/spec/frontend/releases/list/components/release_block_spec.js
@@ -1,5 +1,6 @@
import { mount } from '@vue/test-utils';
import ReleaseBlock from '~/releases/list/components/release_block.vue';
+import ReleaseBlockFooter from '~/releases/list/components/release_block_footer.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { first } from 'underscore';
import { release } from '../../mock_data';
@@ -21,14 +22,16 @@ describe('Release block', () => {
let wrapper;
let releaseClone;
- const factory = (releaseProp, releaseEditPageFeatureFlag = true) => {
+ const factory = (releaseProp, featureFlags = {}) => {
wrapper = mount(ReleaseBlock, {
propsData: {
release: releaseProp,
},
provide: {
glFeatures: {
- releaseEditPage: releaseEditPageFeatureFlag,
+ releaseEditPage: true,
+ releaseIssueSummary: true,
+ ...featureFlags,
},
},
sync: false,
@@ -142,6 +145,10 @@ describe('Release block', () => {
expect(milestoneLink.attributes('data-original-title')).toBe(milestone.description);
});
+
+ it('renders the footer', () => {
+ expect(wrapper.find(ReleaseBlockFooter).exists()).toBe(true);
+ });
});
it('renders commit sha', () => {
@@ -173,7 +180,7 @@ describe('Release block', () => {
});
it('does not render an edit button if the releaseEditPage feature flag is disabled', () =>
- factory(releaseClone, false).then(() => {
+ factory(releaseClone, { releaseEditPage: false }).then(() => {
expect(editButton().exists()).toBe(false);
}));
diff --git a/spec/frontend/releases/mock_data.js b/spec/frontend/releases/mock_data.js
index 32095fd4b78..61d95b86b1c 100644
--- a/spec/frontend/releases/mock_data.js
+++ b/spec/frontend/releases/mock_data.js
@@ -30,6 +30,7 @@ export const milestones = [
export const release = {
name: 'New release',
tag_name: 'v0.3',
+ tag_path: '/root/release-test/-/tags/v0.3',
description: 'A super nice release!',
description_html: '<p data-sourcepos="1:1-1:21" dir="auto">A super nice release!</p>',
created_at: '2019-08-26T17:54:04.952Z',
@@ -56,6 +57,7 @@ export const release = {
committer_email: 'admin@example.com',
committed_date: '2019-08-26T17:47:07.000Z',
},
+ commit_path: '/root/release-test/commit/c22b0728d1b465f82898c884d32b01aa642f96c1',
upcoming_release: false,
milestones,
assets: {
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 59abe8c09e1..172ead158fb 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -76,6 +76,10 @@ describe UsersHelper do
allow(helper).to receive(:can?).and_return(false)
end
+ after do
+ expect(items).not_to include(:start_trial)
+ end
+
it 'includes all default items' do
expect(items).to include(:help, :sign_out)
end
diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js
index bbc59632f3c..766c3378584 100644
--- a/spec/javascripts/blob/viewer/index_spec.js
+++ b/spec/javascripts/blob/viewer/index_spec.js
@@ -176,15 +176,13 @@ describe('Blob viewer', () => {
});
});
- describe('a URL inside the blob content', () => {
- beforeEach(() => {
+ describe('linkifyURLs', () => {
+ it('renders a plain url as a link in simple view', done => {
mock.onGet('http://test.host/snippets/1.json?viewer=simple').reply(200, {
html:
'<div class="js-blob-content"><pre class="code"><code><span class="line" lang="yaml"><span class="c1">To install gitlab-shell you also need a Go compiler version 1.8 or newer. https://golang.org/dl/</span></span></code></pre></div>',
});
- });
- it('is rendered as a link in simple view', done => {
asyncClick()
.then(() => {
expect(document.querySelector('.blob-viewer[data-type="simple"]').innerHTML).toContain(
@@ -197,5 +195,24 @@ describe('Blob viewer', () => {
done();
});
});
+
+ it('leaves an unescaped url untouched', done => {
+ mock.onGet('http://test.host/snippets/1.json?viewer=simple').reply(200, {
+ html:
+ '<div class="js-blob-content"><pre class="code"><code><span class="line" lang="yaml"><a href="https://golang.org/dl/">golang</a></span></span></code></pre></div>',
+ });
+
+ asyncClick()
+ .then(() => {
+ expect(document.querySelector('.blob-viewer[data-type="simple"]').innerHTML).toContain(
+ '<a href="https://golang.org/dl/">golang</a>',
+ );
+ done();
+ })
+ .catch(() => {
+ fail();
+ done();
+ });
+ });
});
});
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index d3eb5a9663f..9fe18caf689 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:result) do
%i[before_script script stage type after_script cache
- image services only except rules variables artifacts
+ image services only except rules needs variables artifacts
environment coverage retry]
end
@@ -384,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do
end
context 'when has needs' do
- context 'that are not a array of strings' do
- let(:config) do
- {
- stage: 'test',
- script: 'echo',
- needs: 'build-job'
- }
- end
-
- it 'returns error about invalid type' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include 'job needs should be an array of strings'
- end
- end
-
context 'when have dependencies that are not subset of needs' do
let(:config) do
{
diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb
new file mode 100644
index 00000000000..d119e604900
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/need_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Gitlab::Ci::Config::Entry::Need do
+ subject(:need) { described_class.new(config) }
+
+ context 'when job is specified' do
+ let(:config) { 'job_name' }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ it 'returns job needs configuration' do
+ expect(need.value).to eq(name: 'job_name')
+ end
+ end
+ end
+
+ context 'when need is empty' do
+ let(:config) { '' }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'is returns an error about an empty config' do
+ expect(need.errors)
+ .to contain_exactly("job config can't be blank")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
new file mode 100644
index 00000000000..f4a76b52d30
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/needs_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ::Gitlab::Ci::Config::Entry::Needs do
+ subject(:needs) { described_class.new(config) }
+
+ before do
+ needs.metadata[:allowed_needs] = %i[job]
+ end
+
+ describe 'validations' do
+ before do
+ needs.compose!
+ end
+
+ context 'when entry config value is correct' do
+ let(:config) { ['job_name'] }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+ end
+
+ context 'when config value has wrong type' do
+ let(:config) { 123 }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors)
+ .to include('needs config can only be a hash or an array')
+ end
+ end
+ end
+
+ context 'when wrong needs type is used' do
+ let(:config) { [123] }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ it 'returns error about incorrect type' do
+ expect(needs.errors).to contain_exactly(
+ 'need has an unsupported type')
+ end
+ end
+ end
+ end
+
+ describe '.compose!' do
+ context 'when valid job entries composed' do
+ let(:config) { %w[first_job_name second_job_name] }
+
+ before do
+ needs.compose!
+ end
+
+ describe '#value' do
+ it 'returns key value' do
+ expect(needs.value).to eq(
+ job: [
+ { name: 'first_job_name' },
+ { name: 'second_job_name' }
+ ]
+ )
+ end
+ end
+
+ describe '#descendants' do
+ it 'creates valid descendant nodes' do
+ expect(needs.descendants.count).to eq 2
+ expect(needs.descendants)
+ .to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/normalizer_spec.rb b/spec/lib/gitlab/ci/config/normalizer_spec.rb
index 6b766cc37bf..bf880478387 100644
--- a/spec/lib/gitlab/ci/config/normalizer_spec.rb
+++ b/spec/lib/gitlab/ci/config/normalizer_spec.rb
@@ -7,6 +7,16 @@ describe Gitlab::Ci::Config::Normalizer do
let(:job_config) { { script: 'rspec', parallel: 5, name: 'rspec' } }
let(:config) { { job_name => job_config } }
+ let(:expanded_job_names) do
+ [
+ "rspec 1/5",
+ "rspec 2/5",
+ "rspec 3/5",
+ "rspec 4/5",
+ "rspec 5/5"
+ ]
+ end
+
describe '.normalize_jobs' do
subject { described_class.new(config).normalize_jobs }
@@ -15,9 +25,7 @@ describe Gitlab::Ci::Config::Normalizer do
end
it 'has parallelized jobs' do
- job_names = [:"rspec 1/5", :"rspec 2/5", :"rspec 3/5", :"rspec 4/5", :"rspec 5/5"]
-
- is_expected.to include(*job_names)
+ is_expected.to include(*expanded_job_names.map(&:to_sym))
end
it 'sets job instance in options' do
@@ -43,49 +51,109 @@ describe Gitlab::Ci::Config::Normalizer do
let(:job_name) { :"rspec 35/2" }
it 'properly parallelizes job names' do
- job_names = [:"rspec 35/2 1/5", :"rspec 35/2 2/5", :"rspec 35/2 3/5", :"rspec 35/2 4/5", :"rspec 35/2 5/5"]
+ job_names = [
+ :"rspec 35/2 1/5",
+ :"rspec 35/2 2/5",
+ :"rspec 35/2 3/5",
+ :"rspec 35/2 4/5",
+ :"rspec 35/2 5/5"
+ ]
is_expected.to include(*job_names)
end
end
- %i[dependencies needs].each do |context|
- context "when job has #{context} on parallelized jobs" do
+ context 'for dependencies' do
+ context "when job has dependencies on parallelized jobs" do
let(:config) do
{
job_name => job_config,
- other_job: { script: 'echo 1', context => [job_name.to_s] }
+ other_job: { script: 'echo 1', dependencies: [job_name.to_s] }
}
end
- it "parallelizes #{context}" do
- job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"]
-
- expect(subject[:other_job][context]).to include(*job_names)
+ it "parallelizes dependencies" do
+ expect(subject[:other_job][:dependencies]).to eq(expanded_job_names)
end
it "does not include original job name in #{context}" do
- expect(subject[:other_job][context]).not_to include(job_name)
+ expect(subject[:other_job][:dependencies]).not_to include(job_name)
end
end
- context "when there are #{context} which are both parallelized and not" do
+ context "when there are dependencies which are both parallelized and not" do
let(:config) do
{
job_name => job_config,
other_job: { script: 'echo 1' },
- final_job: { script: 'echo 1', context => [job_name.to_s, "other_job"] }
+ final_job: { script: 'echo 1', dependencies: [job_name.to_s, "other_job"] }
}
end
- it "parallelizes #{context}" do
+ it "parallelizes dependencies" do
job_names = ["rspec 1/5", "rspec 2/5", "rspec 3/5", "rspec 4/5", "rspec 5/5"]
- expect(subject[:final_job][context]).to include(*job_names)
+ expect(subject[:final_job][:dependencies]).to include(*job_names)
+ end
+
+ it "includes the regular job in dependencies" do
+ expect(subject[:final_job][:dependencies]).to include('other_job')
+ end
+ end
+ end
+
+ context 'for needs' do
+ let(:expanded_job_attributes) do
+ expanded_job_names.map do |job_name|
+ { name: job_name }
+ end
+ end
+
+ context "when job has needs on parallelized jobs" do
+ let(:config) do
+ {
+ job_name => job_config,
+ other_job: {
+ script: 'echo 1',
+ needs: {
+ job: [
+ { name: job_name.to_s }
+ ]
+ }
+ }
+ }
+ end
+
+ it "parallelizes needs" do
+ expect(subject.dig(:other_job, :needs, :job)).to eq(expanded_job_attributes)
+ end
+ end
+
+ context "when there are dependencies which are both parallelized and not" do
+ let(:config) do
+ {
+ job_name => job_config,
+ other_job: {
+ script: 'echo 1'
+ },
+ final_job: {
+ script: 'echo 1',
+ needs: {
+ job: [
+ { name: job_name.to_s },
+ { name: "other_job" }
+ ]
+ }
+ }
+ }
+ end
+
+ it "parallelizes dependencies" do
+ expect(subject.dig(:final_job, :needs, :job)).to include(*expanded_job_attributes)
end
- it "includes the regular job in #{context}" do
- expect(subject[:final_job][context]).to include('other_job')
+ it "includes the regular job in dependencies" do
+ expect(subject.dig(:final_job, :needs, :job)).to include(name: 'other_job')
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index c747ea670bb..dc7bbc519ee 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1293,7 +1293,7 @@ module Gitlab
end
end
- describe "Needs" do
+ describe "Job Needs" do
let(:needs) { }
let(:dependencies) { }
@@ -1301,6 +1301,7 @@ module Gitlab
{
build1: { stage: 'build', script: 'test' },
build2: { stage: 'build', script: 'test' },
+ parallel: { stage: 'build', script: 'test', parallel: 2 },
test1: { stage: 'test', script: 'test', needs: needs, dependencies: dependencies },
test2: { stage: 'test', script: 'test' },
deploy: { stage: 'test', script: 'test' }
@@ -1317,7 +1318,7 @@ module Gitlab
let(:needs) { %w(build1 build2) }
it "does create jobs with valid specification" do
- expect(subject.builds.size).to eq(5)
+ expect(subject.builds.size).to eq(7)
expect(subject.builds[0]).to eq(
stage: "build",
stage_idx: 1,
@@ -1329,16 +1330,11 @@ module Gitlab
allow_failure: false,
yaml_variables: []
)
- expect(subject.builds[2]).to eq(
+ expect(subject.builds[4]).to eq(
stage: "test",
stage_idx: 2,
name: "test1",
- options: {
- script: ["test"],
- # This does not make sense, there is a follow-up:
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/65569
- bridge_needs: %w[build1 build2]
- },
+ options: { script: ["test"] },
needs_attributes: [
{ name: "build1" },
{ name: "build2" }
@@ -1350,10 +1346,25 @@ module Gitlab
end
end
- context 'needs two builds defined as symbols' do
- let(:needs) { [:build1, :build2] }
+ context 'needs parallel job' do
+ let(:needs) { %w(parallel) }
- it { expect { subject }.not_to raise_error }
+ it "does create jobs with valid specification" do
+ expect(subject.builds.size).to eq(7)
+ expect(subject.builds[4]).to eq(
+ stage: "test",
+ stage_idx: 2,
+ name: "test1",
+ options: { script: ["test"] },
+ needs_attributes: [
+ { name: "parallel 1/2" },
+ { name: "parallel 2/2" }
+ ],
+ when: "on_success",
+ allow_failure: false,
+ yaml_variables: []
+ )
+ end
end
context 'undefined need' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 5e5a94f8cda..9295bb993ce 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2893,6 +2893,25 @@ describe Ci::Pipeline, :mailer do
it 'contains yaml errors' do
expect(pipeline).to have_yaml_errors
+ expect(pipeline.yaml_errors).to include('contains unknown keys')
+ end
+ end
+
+ context 'when pipeline has undefined error' do
+ let(:pipeline) do
+ create(:ci_pipeline, config: {})
+ end
+
+ it 'contains yaml errors' do
+ expect(::Gitlab::Ci::YamlProcessor).to receive(:new)
+ .and_raise(RuntimeError, 'undefined failure')
+
+ expect(Gitlab::Sentry).to receive(:track_acceptable_exception)
+ .with(be_a(RuntimeError), anything)
+ .and_call_original
+
+ expect(pipeline).to have_yaml_errors
+ expect(pipeline.yaml_errors).to include('Undefined error')
end
end
diff --git a/spec/services/clusters/destroy_service_spec.rb b/spec/services/clusters/destroy_service_spec.rb
new file mode 100644
index 00000000000..c0fcc971500
--- /dev/null
+++ b/spec/services/clusters/destroy_service_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::DestroyService do
+ describe '#execute' do
+ subject { described_class.new(cluster.user, params).execute(cluster) }
+
+ let!(:cluster) { create(:cluster, :project, :provided_by_user) }
+
+ context 'when correct params' do
+ shared_examples 'only removes cluster' do
+ it 'does not start cleanup' do
+ expect(cluster).not_to receive(:start_cleanup)
+ subject
+ end
+
+ it 'destroys the cluster' do
+ subject
+ expect { cluster.reload }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
+
+ context 'when params are empty' do
+ let(:params) { {} }
+
+ it_behaves_like 'only removes cluster'
+ end
+
+ context 'when cleanup param is false' do
+ let(:params) { { cleanup: 'false' } }
+
+ it_behaves_like 'only removes cluster'
+ end
+
+ context 'when cleanup param is true' do
+ let(:params) { { cleanup: 'true' } }
+
+ before do
+ allow(Clusters::Cleanup::AppWorker).to receive(:perform_async)
+ end
+
+ it 'does not destroy cluster' do
+ subject
+ expect(Clusters::Cluster.where(id: cluster.id).exists?).not_to be_falsey
+ end
+
+ it 'transition cluster#cleanup_status from cleanup_not_started to uninstalling_applications' do
+ expect { subject }.to change { cluster.cleanup_status_name }
+ .from(:cleanup_not_started)
+ .to(:cleanup_uninstalling_applications)
+ end
+ end
+ end
+ end
+end