Commit 78c30dfd authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into ce-to-ee-2018-10-15

parents 8b919b8c a5c7e0a3
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue'; import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue';
import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
...@@ -10,6 +11,7 @@ ...@@ -10,6 +11,7 @@
components: { components: {
UserAvatarLink, UserAvatarLink,
IssueCardWeight, IssueCardWeight,
Icon,
}, },
directives: { directives: {
tooltip, tooltip,
...@@ -142,11 +144,11 @@ ...@@ -142,11 +144,11 @@
<div> <div>
<div class="board-card-header"> <div class="board-card-header">
<h4 class="board-card-title"> <h4 class="board-card-title">
<i <icon
v-if="issue.confidential" v-if="issue.confidential"
class="fa fa-eye-slash confidential-icon" name="eye-slash"
aria-hidden="true" class="confidential-icon"
></i> />
<a <a
:href="issue.path" :href="issue.path"
:title="issue.title" :title="issue.title"
......
<script> <script>
import Icon from '~/vue_shared/components/icon.vue';
import bp from '../../../breakpoints'; import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import IssueCardInner from '../issue_card_inner.vue'; import IssueCardInner from '../issue_card_inner.vue';
...@@ -6,6 +7,7 @@ ...@@ -6,6 +7,7 @@
export default { export default {
components: { components: {
IssueCardInner, IssueCardInner,
Icon,
}, },
props: { props: {
issueLinkBase: { issueLinkBase: {
...@@ -147,13 +149,13 @@ ...@@ -147,13 +149,13 @@
:issue="issue" :issue="issue"
:issue-link-base="issueLinkBase" :issue-link-base="issueLinkBase"
:root-path="rootPath"/> :root-path="rootPath"/>
<span <icon
v-if="issue.selected" v-if="issue.selected"
:aria-label="'Issue #' + issue.id + ' selected'" :aria-label="'Issue #' + issue.id + ' selected'"
name="mobile-issue-close"
aria-checked="true" aria-checked="true"
class="issue-card-selected text-center"> class="issue-card-selected text-center"
<i class="fa fa-check"></i> />
</span>
</div> </div>
</div> </div>
</div> </div>
......
<script> <script>
import { Link } from '@gitlab-org/gitlab-ui'; import { Link } from '@gitlab-org/gitlab-ui';
import Icon from '~/vue_shared/components/icon.vue';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import boardsStore from '../../stores/boards_store'; import boardsStore from '../../stores/boards_store';
export default { export default {
components: { components: {
'gl-link': Link, 'gl-link': Link,
Icon,
}, },
data() { data() {
return { return {
...@@ -35,7 +37,9 @@ export default { ...@@ -35,7 +37,9 @@ export default {
class="dropdown-label-box"> class="dropdown-label-box">
</span> </span>
{{ selected.title }} {{ selected.title }}
<i class="fa fa-chevron-down"></i> <icon
name="chevron-down"
/>
</button> </button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"> <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
<ul> <ul>
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import Api from '../../api'; import Api from '../../api';
export default { export default {
name: 'BoardProjectSelect', name: 'BoardProjectSelect',
components: {
Icon,
},
props: { props: {
groupId: { groupId: {
type: Number, type: Number,
...@@ -78,11 +82,9 @@ export default { ...@@ -78,11 +82,9 @@ export default {
aria-expanded="false" aria-expanded="false"
> >
{{ selectedProjectName }} {{ selectedProjectName }}
<i <icon
class="fa fa-chevron-down" name="chevron-down"
aria-hidden="true" />
>
</i>
</button> </button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width"> <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
<div class="dropdown-title"> <div class="dropdown-title">
...@@ -92,12 +94,11 @@ export default { ...@@ -92,12 +94,11 @@ export default {
type="button" type="button"
class="dropdown-title-button dropdown-menu-close" class="dropdown-title-button dropdown-menu-close"
> >
<i <icon
aria-hidden="true" name="merge-request-close-m"
data-hidden="true" data-hidden="true"
class="fa fa-times dropdown-menu-close-icon" class="dropdown-menu-close-icon"
> />
</i>
</button> </button>
</div> </div>
<div class="dropdown-input"> <div class="dropdown-input">
...@@ -106,12 +107,11 @@ export default { ...@@ -106,12 +107,11 @@ export default {
type="search" type="search"
placeholder="Search projects" placeholder="Search projects"
/> />
<i <icon
aria-hidden="true" name="search"
class="dropdown-input-search"
data-hidden="true" data-hidden="true"
class="fa fa-search dropdown-input-search" />
>
</i>
</div> </div>
<div class="dropdown-content"></div> <div class="dropdown-content"></div>
<div class="dropdown-loading"> <div class="dropdown-loading">
......
<script> <script>
import Icon from '~/vue_shared/components/icon.vue';
import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg'; import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
export default { export default {
components: {
Icon,
},
props: { props: {
documentationLink: { documentationLink: {
type: String, type: String,
...@@ -28,10 +32,9 @@ ...@@ -28,10 +32,9 @@
type="button" type="button"
@click="dismissOverviewDialog" @click="dismissOverviewDialog"
> >
<i <icon
class="fa fa-times" name="close"
aria-hidden="true"> />
</i>
</button> </button>
<div <div
class="svg-container" class="svg-container"
......
...@@ -115,7 +115,7 @@ export default { ...@@ -115,7 +115,7 @@ export default {
<span> <span>
{{ selectedVersionName }} {{ selectedVersionName }}
</span> </span>
<Icon <icon
:size="12" :size="12"
name="angle-down" name="angle-down"
class="position-absolute" class="position-absolute"
......
...@@ -60,11 +60,9 @@ export default { ...@@ -60,11 +60,9 @@ export default {
> >
<span> <span>
<icon name="play" /> <icon name="play" />
<i <icon
class="fa fa-caret-down" name="chevron-down"
aria-hidden="true" />
>
</i>
<gl-loading-icon v-if="isLoading" /> <gl-loading-icon v-if="isLoading" />
</span> </span>
</button> </button>
......
...@@ -4,6 +4,7 @@ import _ from 'underscore'; ...@@ -4,6 +4,7 @@ import _ from 'underscore';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { humanize } from '~/lib/utils/text_utility'; import { humanize } from '~/lib/utils/text_utility';
import Icon from '~/vue_shared/components/icon.vue';
import ActionsComponent from './environment_actions.vue'; import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue'; import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue'; import StopComponent from './environment_stop.vue';
...@@ -24,6 +25,7 @@ export default { ...@@ -24,6 +25,7 @@ export default {
components: { components: {
UserAvatarLink, UserAvatarLink,
CommitComponent, CommitComponent,
Icon,
ActionsComponent, ActionsComponent,
ExternalUrlComponent, ExternalUrlComponent,
StopComponent, StopComponent,
...@@ -448,6 +450,14 @@ export default { ...@@ -448,6 +450,14 @@ export default {
this.canRetry this.canRetry
); );
}, },
folderIconName() {
return this.model.isOpen ? 'chevron-down' : 'chevron-right';
},
deployIconName() {
return this.model.isDeployBoardVisible ? 'chevron-down' : 'chevron-right';
},
}, },
methods: { methods: {
...@@ -481,23 +491,15 @@ export default { ...@@ -481,23 +491,15 @@ export default {
> >
{{ s__("Environments|Environment") }} {{ s__("Environments|Environment") }}
</div> </div>
<span <span
v-if="model.hasDeployBoard" v-if="model.hasDeployBoard"
class="deploy-board-icon" class="deploy-board-icon"
@click="toggleDeployBoard"> @click="toggleDeployBoard"
>
<i <icon :name="deployIconName" />
v-show="!model.isDeployBoardVisible"
class="fa fa-caret-right"
aria-hidden="true">
</i>
<i
v-show="model.isDeployBoardVisible"
class="fa fa-caret-down"
aria-hidden="true">
</i>
</span> </span>
<span <span
v-if="!model.isFolder" v-if="!model.isFolder"
class="environment-name table-mobile-content"> class="environment-name table-mobile-content">
...@@ -520,27 +522,15 @@ export default { ...@@ -520,27 +522,15 @@ export default {
role="button" role="button"
@click="onClickFolder"> @click="onClickFolder">
<span class="folder-icon"> <icon
<i :name="folderIconName"
v-show="model.isOpen" class="folder-icon"
class="fa fa-caret-down" />
aria-hidden="true"
>
</i>
<i
v-show="!model.isOpen"
class="fa fa-caret-right"
aria-hidden="true"
>
</i>
</span>
<span class="folder-icon"> <icon
<i name="folder"
class="fa fa-folder" class="folder-icon"
aria-hidden="true"> />
</i>
</span>
<span> <span>
{{ model.folderName }} {{ model.folderName }}
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import frequentItemsMixin from './frequent_items_mixin'; import frequentItemsMixin from './frequent_items_mixin';
export default { export default {
components: {
Icon,
},
mixins: [frequentItemsMixin], mixins: [frequentItemsMixin],
data() { data() {
return { return {
...@@ -45,11 +49,10 @@ export default { ...@@ -45,11 +49,10 @@ export default {
type="search" type="search"
class="form-control" class="form-control"
/> />
<i <icon
v-if="!searchQuery" v-if="!searchQuery"
class="search-icon fa fa-fw fa-search" name="search"
aria-hidden="true" class="search-icon"
> />
</i>
</div> </div>
</template> </template>
...@@ -102,7 +102,6 @@ export default { ...@@ -102,7 +102,6 @@ export default {
> >
<icon <icon
:name="lockIcon" :name="lockIcon"
aria-hidden="true"
class="sidebar-item-icon is-active" class="sidebar-item-icon is-active"
/> />
</div> </div>
...@@ -134,7 +133,6 @@ export default { ...@@ -134,7 +133,6 @@ export default {
<icon <icon
:size="16" :size="16"
name="lock" name="lock"
aria-hidden="true"
class="sidebar-item-icon inline is-active" class="sidebar-item-icon inline is-active"
/> />
{{ __('Locked') }} {{ __('Locked') }}
...@@ -147,7 +145,6 @@ export default { ...@@ -147,7 +145,6 @@ export default {
<icon <icon
:size="16" :size="16"
name="lock-open" name="lock-open"
aria-hidden="true"
class="sidebar-item-icon inline" class="sidebar-item-icon inline"
/> />
{{ __('Unlocked') }} {{ __('Unlocked') }}
......
...@@ -105,6 +105,7 @@ export default { ...@@ -105,6 +105,7 @@ export default {
:x="x" :x="x"
:y="y" :y="y"
:tabindex="tabIndex" :tabindex="tabIndex"
aria-hidden="true"
> >
<use v-bind="{ 'xlink:href':spriteHref }"/> <use v-bind="{ 'xlink:href':spriteHref }"/>
</svg> </svg>
......
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
:name="warningIcon" :name="warningIcon"
:size="16" :size="16"
class="icon inline" class="icon inline"
aria-hidden="true"
/> />
<span v-if="isLockedAndConfidential"> <span v-if="isLockedAndConfidential">
......
...@@ -109,7 +109,7 @@ export default { ...@@ -109,7 +109,7 @@ export default {
class="system-note-commit-list-toggler flex-row" class="system-note-commit-list-toggler flex-row"
@click="expanded = !expanded" @click="expanded = !expanded"
> >
<Icon <icon
:name="toggleIcon" :name="toggleIcon"
:size="8" :size="8"
class="append-right-5" class="append-right-5"
......
...@@ -283,18 +283,20 @@ ...@@ -283,18 +283,20 @@
.dismiss-button { .dismiss-button {
position: absolute; position: absolute;
right: 6px; right: $gl-padding-8;
top: 6px; top: $gl-padding-8;
cursor: pointer; cursor: pointer;
color: $blue-300; color: $blue-500;
z-index: 1; z-index: 1;
border: 0; border: 0;
background-color: transparent; background-color: transparent;
padding: $gl-padding-8;
line-height: 0;
&:hover, &:hover,
&:focus { &:focus {
border: 0; border: 0;
color: $blue-400; color: $blue-700;
} }
} }
......
...@@ -144,6 +144,13 @@ ...@@ -144,6 +144,13 @@
top: 11px; top: 11px;
right: 8px; right: 8px;
} }
.ic-chevron-down {
position: absolute;
top: $gl-padding-8;
right: $gl-padding-8;
color: $gray-darkest;
}
} }
@mixin dropdown-item-hover { @mixin dropdown-item-hover {
...@@ -561,6 +568,10 @@ ...@@ -561,6 +568,10 @@
top: -1px; top: -1px;
} }
.dropdown-menu-close-icon {
vertical-align: middle;
}
.dropdown-menu-back { .dropdown-menu-back {
left: 7px; left: 7px;
top: 2px; top: 2px;
...@@ -572,9 +583,10 @@ ...@@ -572,9 +583,10 @@
padding: 0 10px; padding: 0 10px;
.fa, .fa,
.input-icon { .input-icon,
.ic-search {
position: absolute; position: absolute;
top: 10px; top: $gl-padding-8;
right: 20px; right: 20px;
color: $dropdown-input-fa-color; color: $dropdown-input-fa-color;
font-size: 12px; font-size: 12px;
......
...@@ -350,8 +350,7 @@ ...@@ -350,8 +350,7 @@
} }
.confidential-icon { .confidential-icon {
position: relative; vertical-align: text-top;
top: 1px;
margin-right: 5px; margin-right: 5px;
} }
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
} }
} }
svg { .svg-container svg {
width: 136px; width: 136px;
height: 136px; height: 136px;
} }
......
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
margin-right: 3px; margin-right: 3px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
display: inline-block; display: inline-block;
vertical-align: text-top;
.fa:nth-child(1) { .fa:nth-child(1) {
margin-right: 3px; margin-right: 3px;
......
...@@ -240,7 +240,7 @@ table.pipeline-project-metrics tr td { ...@@ -240,7 +240,7 @@ table.pipeline-project-metrics tr td {
} }
} }
svg { .svg-container svg {
width: 62px; width: 62px;
height: 50px; height: 50px;
} }
......
...@@ -10,7 +10,7 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -10,7 +10,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:keep] before_action :authorize_update_build!, only: [:keep]
before_action :extract_ref_name_and_path before_action :extract_ref_name_and_path
before_action :set_request_format, only: [:file] before_action :set_request_format, only: [:file]
before_action :validate_artifacts! before_action :validate_artifacts!, except: [:download]
before_action :entry, only: [:file] before_action :entry, only: [:file]
def download def download
...@@ -102,7 +102,7 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -102,7 +102,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def artifacts_file def artifacts_file
@artifacts_file ||= build.artifacts_file_for_type(params[:file_type] || :archive) @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive)
end end
def entry def entry
......
...@@ -6,7 +6,7 @@ class Projects::BuildArtifactsController < Projects::ApplicationController ...@@ -6,7 +6,7 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
before_action :authorize_read_build! before_action :authorize_read_build!
before_action :extract_ref_name_and_path before_action :extract_ref_name_and_path
before_action :validate_artifacts! before_action :validate_artifacts!, except: [:download]
def download def download
redirect_to download_project_job_artifacts_path(project, job) redirect_to download_project_job_artifacts_path(project, job)
......
...@@ -19,7 +19,7 @@ module Clusters ...@@ -19,7 +19,7 @@ module Clusters
def set_initial_status def set_initial_status
return unless not_installable? return unless not_installable?
if cluster&.application_ingress_installed? && cluster.application_ingress.external_ip if cluster&.application_ingress_available? && cluster.application_ingress.external_ip
self.status = 'installable' self.status = 'installable'
end end
end end
......
...@@ -45,8 +45,9 @@ module Clusters ...@@ -45,8 +45,9 @@ module Clusters
delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true
delegate :rbac?, to: :platform_kubernetes, prefix: true, allow_nil: true delegate :rbac?, to: :platform_kubernetes, prefix: true, allow_nil: true
delegate :installed?, to: :application_helm, prefix: true, allow_nil: true delegate :available?, to: :application_helm, prefix: true, allow_nil: true
delegate :installed?, to: :application_ingress, prefix: true, allow_nil: true delegate :available?, to: :application_ingress, prefix: true, allow_nil: true
delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
enum platform_type: { enum platform_type: {
kubernetes: 1 kubernetes: 1
......
...@@ -15,7 +15,7 @@ module Clusters ...@@ -15,7 +15,7 @@ module Clusters
def set_initial_status def set_initial_status
return unless not_installable? return unless not_installable?
self.status = 'installable' if cluster&.application_helm_installed? self.status = 'installable' if cluster&.application_helm_available?
end end
def self.application_name def self.application_name
......
...@@ -66,6 +66,10 @@ module Clusters ...@@ -66,6 +66,10 @@ module Clusters
end end
end end
end end
def available?
installed? || updated?
end
end end
end end
end end
...@@ -26,7 +26,7 @@ class PrometheusService < MonitoringService ...@@ -26,7 +26,7 @@ class PrometheusService < MonitoringService
end end
def editable? def editable?
manual_configuration? || !prometheus_installed? manual_configuration? || !prometheus_available?
end end
def title def title
...@@ -75,17 +75,17 @@ class PrometheusService < MonitoringService ...@@ -75,17 +75,17 @@ class PrometheusService < MonitoringService
RestClient::Resource.new(api_url) if api_url && manual_configuration? && active? RestClient::Resource.new(api_url) if api_url && manual_configuration? && active?
end end
def prometheus_installed? def prometheus_available?
return false if template? return false if template?
return false unless project return false unless project
project.clusters.enabled.any? { |cluster| cluster.application_prometheus&.installed? } project.clusters.enabled.any? { |cluster| cluster.application_prometheus_available? }
end end
private private
def synchronize_service_state def synchronize_service_state
self.active = prometheus_installed? || manual_configuration? self.active = prometheus_available? || manual_configuration?
true true
end end
......
...@@ -212,7 +212,7 @@ module SystemNoteService ...@@ -212,7 +212,7 @@ module SystemNoteService
# "closed via bc17db76" # "closed via bc17db76"
# #
# Returns the created Note object # Returns the created Note object
def change_status(noteable, project, author, status, source) def change_status(noteable, project, author, status, source = nil)
body = status.dup body = status.dup
body << " via #{source.gfm_reference(project)}" if source body << " via #{source.gfm_reference(project)}" if source
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= form_errors(@group) = form_errors(@group)
= render 'shared/group_form', f: f = render 'shared/group_form', f: f
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group = render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group
= render_if_exists 'admin/namespace_plan', f: f = render_if_exists 'admin/namespace_plan', f: f
.form-group.row.group-description-holder .form-group.row.group-description-holder
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
- if cookies[:explore_groups_landing_dismissed] != 'true' - if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hide .explore-groups.landing.content-block.js-explore-groups-landing.hide
%button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= icon('times') %button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= sprite_icon('close', size: 16)
.svg-container .svg-container
= custom_icon('icon_explore_groups_splash') = custom_icon('icon_explore_groups_splash')
.inner-content .inner-content
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
%br/ %br/
%span.descr This setting can be overridden in each project. %span.descr This setting can be overridden in each project.
= render partial: 'groups/ee/project_creation_level', locals: { form: f, group: @group } = render partial: 'groups/ee/old_project_creation_level', locals: { form: f, group: @group }
.form-group.row .form-group.row
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0' = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2 pt-0'
......
...@@ -13,11 +13,11 @@ ...@@ -13,11 +13,11 @@
= f.text_field :id, class: 'form-control w-auto', readonly: true = f.text_field :id, class: 'form-control w-auto', readonly: true
.row.prepend-top-8 .row.prepend-top-8
.form-group.col-md-9.append-bottom-0 .form-group.col-md-9
= f.label :description, _('Group description (optional)'), class: 'label-bold' = f.label :description, _('Group description (optional)'), class: 'label-bold'
= f.text_area :description, class: 'form-control', rows: 3, maxlength: 250 = f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group .row= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
.form-group.prepend-top-default.append-bottom-20 .form-group.prepend-top-default.append-bottom-20
.avatar-container.s90 .avatar-container.s90
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
%span.descr.text-muted= share_with_group_lock_help_text(@group) %span.descr.text-muted= share_with_group_lock_help_text(@group)
= render 'groups/settings/lfs', f: f = render 'groups/settings/lfs', f: f
= render partial: 'groups/ee/project_creation_level', locals: { form: f, group: @group }
= render 'groups/settings/two_factor_auth', f: f = render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/member_lock_setting', f: f, group: @group = render_if_exists 'groups/member_lock_setting', f: f, group: @group
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- else - else
.container-fluid .container-fluid
.row .row
- if service.prometheus_installed? - if service.prometheus_available?
.col-sm-2 .col-sm-2
.svg-container .svg-container
= image_tag 'illustrations/monitoring/getting_started.svg' = image_tag 'illustrations/monitoring/getting_started.svg'
......
---
title: Show available clusters when installed or updated
merge_request: 22356
author:
type: fixed
---
title: Replace i to icons in vue components
merge_request: 20748
author: George Tsiolis
type: changed
...@@ -273,6 +273,67 @@ edit existing comments. Non-team members are restricted from adding or editing c ...@@ -273,6 +273,67 @@ edit existing comments. Non-team members are restricted from adding or editing c
Additionally locked issues can not be reopened. Additionally locked issues can not be reopened.
## Merge Request Reviews **[PREMIUM]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4213) in GitLab 11.4.
When looking at a Merge Request diff, you are able to start a review.
This allows you to create comments inside a Merge Request that are **only visible to you** until published,
in order to allow you to submit them all as a single action.
### Starting a review
In order to start a review, simply write a comment on a diff as normal under the **Changes** tab
in an MR and click on the **Start a review** button.
![Starting a review](img/mr_review_start.png)
Once a review is started, you will see any comments that are part of this review marked `Pending`.
All comments that are part of a review show two buttons:
- **Submit review**: Submits all comments that are part of the review, making them visible to other users.
- **Add comment now**: Submits the specific comment as a regular comment instead of as part of the review.
![A comment that is part of a review](img/pending_review_comment.png)
You can use [quick actions] inside review comments. The comment will show the actions that will be performed once published.
![A review comment with quick actions](img/review_comment_quickactions.png)
To add more comments to a review, start writing a comment as normal and click the **Add to review** button.
![Adding a second comment to a review](img/mr_review_second_comment.png)
This will add the comment to the review.
![Second review comment](img/mr_review_second_comment_added.png)
### Resolving/Unresolving discussions
Review comments can also resolve/unresolve [resolvable discussions](#resolvable-discussions).
When replying to a comment, you will see a checkbox that you can click in order to resolve or unresolve
the discussion once published.
![Resolve checkbox](img/mr_review_resolve.png)
![Unresolve checkbox](img/mr_review_unresolve.png)
If a particular pending comment will resolve or unresolve the discussion, this will be shown on the pending
comment itself.
![Resolve status](img/mr_review_resolve2.png)
![Unresolve status](img/mr_review_unresolve2.png)
### Submitting a review
If you have any comments that have not been submitted, you will see a bar at the bottom of the screen with two buttons:
- **Discard**: Discards all comments that have not been submitted.
- **Submit review**: All comments are published. Any quick actions submitted are performed at this time.
Alternatively, every pending comment has a button to submit the entire review.
![MR Review bar](img/mr_review_bar.png)
[ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022 [ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022
[ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125 [ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125
[ce-7527]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7527 [ce-7527]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7527
......
...@@ -86,10 +86,11 @@ website with GitLab Pages ...@@ -86,10 +86,11 @@ website with GitLab Pages
- [Cycle Analytics](cycle_analytics.md): Review your development lifecycle - [Cycle Analytics](cycle_analytics.md): Review your development lifecycle
- [Security Dashboard](security_dashboard.md): Security Dashboard - [Security Dashboard](security_dashboard.md): Security Dashboard
- [Syntax highlighting](highlighting.md): An alternative to customize - [Syntax highlighting](highlighting.md): An alternative to customize
your code blocks, overriding GitLab's default choice of language your code blocks, overriding GitLab's default choice of language
- [Badges](badges.md): Badges for the project overview - [Badges](badges.md): Badges for the project overview
- [Maven packages](packages/maven_repository.md): Your private Maven repository in GitLab **[PREMIUM]** - [Maven packages](packages/maven_repository.md): Your private Maven repository in GitLab **[PREMIUM]**
- [Code owners](code_owners.md): Specify code owners for certain files **[STARTER]** - [Code owners](code_owners.md): Specify code owners for certain files **[STARTER]**
- [License Management](merge_requests/license_management.md): Approve and blacklist licenses for projects **[ULTIMATE]**
### Project's integrations ### Project's integrations
......
...@@ -33,6 +33,7 @@ With GitLab merge requests, you can: ...@@ -33,6 +33,7 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also: With **[GitLab Enterprise Edition][ee]**, you can also:
- Prepare a full review and submit it once it's ready with [Merge Request Reviews](../../discussions/index.md#merge-request-reviews) **[PREMIUM]**
- View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md#multi-project-pipeline-graphs) **[PREMIUM]** - View the deployment process across projects with [Multi-Project Pipelines](../../../ci/multi_project_pipelines.md#multi-project-pipeline-graphs) **[PREMIUM]**
- Request [approvals](merge_request_approvals.md) from your managers **[STARTER]** - Request [approvals](merge_request_approvals.md) from your managers **[STARTER]**
- Analyze the impact of your changes with [Code Quality reports](code_quality.md) **[STARTER]** - Analyze the impact of your changes with [Code Quality reports](code_quality.md) **[STARTER]**
...@@ -149,6 +150,14 @@ you hide discussions that are no longer relevant. ...@@ -149,6 +150,14 @@ you hide discussions that are no longer relevant.
[Read more about resolving discussion comments in merge requests reviews.](../../discussions/index.md) [Read more about resolving discussion comments in merge requests reviews.](../../discussions/index.md)
## Perform a Review **[PREMIUM]**
Start a review in order to create multiple comments on a diff and publish them once you're ready.
Starting a review allows you to get all your thoughts in order and ensure you haven't missed anything
before submitting all your comments.
[Learn more about Merge Request Reviews](../../discussions/index.md#merge-request-reviews)
## Squash and merge ## Squash and merge
GitLab allows you to squash all changes present in a merge request into a single GitLab allows you to squash all changes present in a merge request into a single
......
...@@ -5,16 +5,17 @@ ...@@ -5,16 +5,17 @@
## Overview ## Overview
If you are using [GitLab CI/CD][ci], you can search your project dependencies for their licenses If you are using [GitLab CI/CD][ci], you can search your project dependencies for their licenses
using License Management, either by using License Management by:
including the CI job in your [existing `.gitlab-ci.yml` file][cc-docs] or
by implicitly using [Auto License Management](../../../topics/autodevops/index.md#auto-dependency-scanning)
that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
Going a step further, GitLab can show the licenses list right in the merge - Including the CI job in your [existing `.gitlab-ci.yml` file][cc-docs].
- Implicitly using [Auto License Management](../../../topics/autodevops/index.md#auto-dependency-scanning)
that is provided by [Auto DevOps](../../../topics/autodevops/index.md).
In addition, you can [manually approve or blacklist](#manual-license-management) licenses in the project's settings.
GitLab can show the licenses list right in the merge
request widget area, highlighting the presence of licenses you don't want to use, or new request widget area, highlighting the presence of licenses you don't want to use, or new
ones that need a decision. ones that need a decision.
Licenses can be accepted or blacklisted in the project settings, or directly from the
merge request widget.
## Use cases ## Use cases
...@@ -37,7 +38,7 @@ The following languages and package managers are supported. ...@@ -37,7 +38,7 @@ The following languages and package managers are supported.
## How it works ## How it works
First of all, you need to define a job named `license_management` in your First, you need to define a job named `license_management` in your
`.gitlab-ci.yml` file. [Check how the `license_management` job should look like][cc-docs]. `.gitlab-ci.yml` file. [Check how the `license_management` job should look like][cc-docs].
In order for the report to show in the merge request, there are two In order for the report to show in the merge request, there are two
...@@ -50,7 +51,7 @@ prerequisites: ...@@ -50,7 +51,7 @@ prerequisites:
>**Note:** >**Note:**
If the license management report doesn't have anything to compare to, no information If the license management report doesn't have anything to compare to, no information
will be displayed in the merge request area. That is the case when you add the will be displayed in the merge request area. That is the case when you add the
`license_management` job in your `.gitlab-ci.yml` for the very first time. `license_management` job in your `.gitlab-ci.yml` for the first time.
Consecutive merge requests will have something to compare to and the license Consecutive merge requests will have something to compare to and the license
management report will be shown properly. management report will be shown properly.
...@@ -67,10 +68,31 @@ the choice to approve it or blacklist it. ...@@ -67,10 +68,31 @@ the choice to approve it or blacklist it.
![License approval decision](img/license_management_decision.png) ![License approval decision](img/license_management_decision.png)
The list of licenses and their status can also be managed from the project settings. From the project's settings:
- The list of licenses and their status can be managed.
- Licenses can be [manually approved or blacklisted](#manual-license-management).
![License Management Settings](img/license_management_settings.png) ![License Management Settings](img/license_management_settings.png)
### Manual license management
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5940) in [GitLab Ultimate][ee] 11.4.
Licenses can be manually approved or blacklisted in a project's settings.
To approve or blacklist a license:
1. Navigate to the project's **Settings > CI/CD**.
1. Expand the **License Management** section and click the **Add a license** button.
1. In the **License name** dropdown, either:
- Select one of the available licenses. You can search for licenses in the field
at the top of the list.
- Enter arbitrary text in the field at the top of the list. This will cause the text to be
added as a license name to the list.
1. Select the **Approve** or **Blacklist** radio button to approve or blacklist respectively
the selected license.
## License Management report under pipelines ## License Management report under pipelines
> [Introduced][ee-5491] in [GitLab Ultimate][ee] 11.2. > [Introduced][ee-5491] in [GitLab Ultimate][ee] 11.2.
......
...@@ -22,7 +22,7 @@ module EE ...@@ -22,7 +22,7 @@ module EE
private private
def prometheus_adapter def prometheus_adapter
return unless cluster&.application_prometheus&.installed? return unless cluster&.application_prometheus_available?
cluster.application_prometheus cluster.application_prometheus
end end
......
...@@ -29,37 +29,6 @@ module EE ...@@ -29,37 +29,6 @@ module EE
group_epic_path(entity.group, entity, *args) group_epic_path(entity.group, entity, *args)
end end
def sast_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project,
pipeline.sast_artifact,
path: Ci::Build::SAST_FILE)
end
def dependency_scanning_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project,
pipeline.dependency_scanning_artifact,
path: Ci::Build::DEPENDENCY_SCANNING_FILE)
end
# sast_container_artifact_url is deprecated and replaced with container_scanning_artifact_url (#5778)
def sast_container_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project,
pipeline.sast_container_artifact,
path: Ci::Build::SAST_CONTAINER_FILE)
end
def container_scanning_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project,
pipeline.container_scanning_artifact,
path: Ci::Build::CONTAINER_SCANNING_FILE)
end
def dast_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project,
pipeline.dast_artifact,
path: Ci::Build::DAST_FILE)
end
def license_management_artifact_url(pipeline) def license_management_artifact_url(pipeline)
raw_project_build_artifacts_url(pipeline.project, raw_project_build_artifacts_url(pipeline.project,
pipeline.license_management_artifact, pipeline.license_management_artifact,
......
...@@ -148,21 +148,12 @@ module EE ...@@ -148,21 +148,12 @@ module EE
can_create_issue: "false" can_create_issue: "false"
} }
else else
# Handle old job and artifact names for container scanning
sast_container_head_path = if pipeline.expose_sast_container_data?
sast_container_artifact_url(pipeline)
elsif pipeline.expose_container_scanning_data?
container_scanning_artifact_url(pipeline)
else
nil
end
{ {
head_blob_path: project_blob_path(project, pipeline.sha), head_blob_path: project_blob_path(project, pipeline.sha),
sast_head_path: pipeline.expose_sast_data? ? sast_artifact_url(pipeline) : nil, sast_head_path: pipeline.downloadable_path_for_report_type(:sast),
dependency_scanning_head_path: pipeline.expose_dependency_scanning_data? ? dependency_scanning_artifact_url(pipeline) : nil, dependency_scanning_head_path: pipeline.downloadable_path_for_report_type(:dependency_scanning),
dast_head_path: pipeline.expose_dast_data? ? dast_artifact_url(pipeline) : nil, dast_head_path: pipeline.downloadable_path_for_report_type(:dast),
sast_container_head_path: sast_container_head_path, sast_container_head_path: pipeline.downloadable_path_for_report_type(:container_scanning),
vulnerability_feedback_path: project_vulnerability_feedback_index_path(project), vulnerability_feedback_path: project_vulnerability_feedback_index_path(project),
pipeline_id: pipeline.id, pipeline_id: pipeline.id,
vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"), vulnerability_feedback_help_path: help_page_path("user/project/merge_requests/index", anchor: "interacting-with-security-reports-ultimate"),
......
...@@ -7,14 +7,8 @@ module EE ...@@ -7,14 +7,8 @@ module EE
module Build module Build
extend ActiveSupport::Concern extend ActiveSupport::Concern
DEPENDENCY_SCANNING_FILE = 'gl-dependency-scanning-report.json'.freeze
LICENSE_MANAGEMENT_FILE = 'gl-license-management-report.json'.freeze LICENSE_MANAGEMENT_FILE = 'gl-license-management-report.json'.freeze
SAST_FILE = 'gl-sast-report.json'.freeze
PERFORMANCE_FILE = 'performance.json'.freeze PERFORMANCE_FILE = 'performance.json'.freeze
# SAST_CONTAINER_FILE is deprecated and replaced with CONTAINER_SCANNING_FILE (#5778)
SAST_CONTAINER_FILE = 'gl-sast-container-report.json'.freeze
CONTAINER_SCANNING_FILE = 'gl-container-scanning-report.json'.freeze
DAST_FILE = 'gl-dast-report.json'.freeze
prepended do prepended do
after_save :stick_build_if_status_changed after_save :stick_build_if_status_changed
...@@ -36,37 +30,11 @@ module EE ...@@ -36,37 +30,11 @@ module EE
has_artifact?(PERFORMANCE_FILE) has_artifact?(PERFORMANCE_FILE)
end end
def has_sast_json?
name_in?('sast') &&
has_artifact?(SAST_FILE)
end
def has_dependency_scanning_json?
name_in?('dependency_scanning') &&
has_artifact?(DEPENDENCY_SCANNING_FILE)
end
def has_license_management_json? def has_license_management_json?
name_in?('license_management') && name_in?('license_management') &&
has_artifact?(LICENSE_MANAGEMENT_FILE) has_artifact?(LICENSE_MANAGEMENT_FILE)
end end
# has_sast_container_json? is deprecated and replaced with has_container_scanning_json? (#5778)
def has_sast_container_json?
name_in?(%w[sast:container container_scanning]) &&
has_artifact?(SAST_CONTAINER_FILE)
end
def has_container_scanning_json?
name_in?(%w[sast:container container_scanning]) &&
has_artifact?(CONTAINER_SCANNING_FILE)
end
def has_dast_json?
name_in?('dast') &&
has_artifact?(DAST_FILE)
end
def log_geo_deleted_event def log_geo_deleted_event
# It is not needed to generate a Geo deleted event # It is not needed to generate a Geo deleted event
# since Legacy Artifacts are migrated to multi-build artifacts # since Legacy Artifacts are migrated to multi-build artifacts
......
...@@ -17,6 +17,16 @@ module EE ...@@ -17,6 +17,16 @@ module EE
joins(:artifacts).where(ci_builds: { name: %w[sast dependency_scanning sast:container container_scanning dast] }) joins(:artifacts).where(ci_builds: { name: %w[sast dependency_scanning sast:container container_scanning dast] })
} }
# This structure describes feature levels
# to access the file types for given reports
LEGACY_REPORT_LICENSED_FEATURES = {
codequality: nil,
sast: :sast,
dependency_scanning: :dependency_scanning,
container_scanning: :sast_container,
dast: :dast
}.freeze
# Deprecated, to be removed in 12.0 # Deprecated, to be removed in 12.0
# A hash of Ci::JobArtifact file_types # A hash of Ci::JobArtifact file_types
# With mapping to the legacy job names, # With mapping to the legacy job names,
...@@ -25,15 +35,39 @@ module EE ...@@ -25,15 +35,39 @@ module EE
codequality: { codequality: {
names: %w(codeclimate codequality code_quality), names: %w(codeclimate codequality code_quality),
files: %w(codeclimate.json gl-code-quality-report.json) files: %w(codeclimate.json gl-code-quality-report.json)
},
sast: {
names: %w(deploy sast),
files: %w(gl-sast-report.json)
},
dependency_scanning: {
names: %w(dependency_scanning),
files: %w(gl-dependency-scanning-report.json)
},
container_scanning: {
names: %w(sast:container container_scanning),
files: %w(gl-sast-container-report.json gl-container-scanning-report.json)
},
dast: {
names: %w(dast),
files: %w(gl-dast-report.json)
} }
}.freeze }.freeze
end end
def artifact_for_file_type(file_type) def any_report_artifact_for_type(file_type)
report_artifact_for_file_type(file_type) || legacy_report_artifact_for_file_type(file_type)
end
def report_artifact_for_file_type(file_type)
return unless available_licensed_report_type?(file_type)
job_artifacts.where(file_type: ::Ci::JobArtifact.file_types[file_type]).last job_artifacts.where(file_type: ::Ci::JobArtifact.file_types[file_type]).last
end end
def legacy_report_artifact_for_file_type(file_type) def legacy_report_artifact_for_file_type(file_type)
return unless available_licensed_report_type?(file_type)
legacy_names = LEGACY_REPORT_FORMATS[file_type] legacy_names = LEGACY_REPORT_FORMATS[file_type]
return unless legacy_names return unless legacy_names
...@@ -53,106 +87,35 @@ module EE ...@@ -53,106 +87,35 @@ module EE
@performance_artifact ||= artifacts_with_files.find(&:has_performance_json?) @performance_artifact ||= artifacts_with_files.find(&:has_performance_json?)
end end
def sast_artifact
@sast_artifact ||= artifacts_with_files.find(&:has_sast_json?)
end
def dependency_scanning_artifact
@dependency_scanning_artifact ||= artifacts_with_files.find(&:has_dependency_scanning_json?)
end
def license_management_artifact def license_management_artifact
@license_management_artifact ||= artifacts_with_files.find(&:has_license_management_json?) @license_management_artifact ||= artifacts_with_files.find(&:has_license_management_json?)
end end
# sast_container_artifact is deprecated and replaced with container_scanning_artifact (#5778)
def sast_container_artifact
@sast_container_artifact ||= artifacts_with_files.find(&:has_sast_container_json?)
end
def container_scanning_artifact
@container_scanning_artifact ||= artifacts_with_files.find(&:has_container_scanning_json?)
end
def dast_artifact
@dast_artifact ||= artifacts_with_files.find(&:has_dast_json?)
end
def has_sast_data?
sast_artifact&.success?
end
def has_dependency_scanning_data?
dependency_scanning_artifact&.success?
end
def has_license_management_data? def has_license_management_data?
license_management_artifact&.success? license_management_artifact&.success?
end end
# has_sast_container_data? is deprecated and replaced with has_container_scanning_data? (#5778)
def has_sast_container_data?
sast_container_artifact&.success?
end
def has_container_scanning_data?
container_scanning_artifact&.success?
end
def has_dast_data?
dast_artifact&.success?
end
def has_performance_data? def has_performance_data?
performance_artifact&.success? performance_artifact&.success?
end end
def expose_sast_data?
project.feature_available?(:sast) &&
has_sast_data?
end
def expose_dependency_scanning_data?
project.feature_available?(:dependency_scanning) &&
has_dependency_scanning_data?
end
def expose_license_management_data? def expose_license_management_data?
project.feature_available?(:license_management) && project.feature_available?(:license_management) &&
has_license_management_data? has_license_management_data?
end end
# expose_sast_container_data? is deprecated and replaced with expose_container_scanning_data? (#5778)
def expose_sast_container_data?
project.feature_available?(:sast_container) &&
has_sast_container_data?
end
def expose_container_scanning_data?
project.feature_available?(:sast_container) &&
has_container_scanning_data?
end
def expose_dast_data?
project.feature_available?(:dast) &&
has_dast_data?
end
def expose_performance_data? def expose_performance_data?
project.feature_available?(:merge_request_performance_metrics) && project.feature_available?(:merge_request_performance_metrics) &&
has_performance_data? has_performance_data?
end end
def expose_security_dashboard?
expose_sast_data? ||
expose_dependency_scanning_data? ||
expose_dast_data? ||
expose_sast_container_data? ||
expose_container_scanning_data?
end
private private
def available_licensed_report_type?(file_type)
feature_name = LEGACY_REPORT_LICENSED_FEATURES.fetch(file_type)
feature_name.nil? || project.feature_available?(feature_name)
end
def artifacts_with_files def artifacts_with_files
@artifacts_with_files ||= artifacts.includes(:job_artifacts_metadata, :job_artifacts_archive).to_a @artifacts_with_files ||= artifacts.includes(:job_artifacts_metadata, :job_artifacts_archive).to_a
end end
......
...@@ -13,35 +13,12 @@ module EE ...@@ -13,35 +13,12 @@ module EE
delegate :performance_artifact, to: :head_pipeline, prefix: :head, allow_nil: true delegate :performance_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :performance_artifact, to: :base_pipeline, prefix: :base, allow_nil: true delegate :performance_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :sast_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :sast_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :dependency_scanning_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :dependency_scanning_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :license_management_artifact, to: :head_pipeline, prefix: :head, allow_nil: true delegate :license_management_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :license_management_artifact, to: :base_pipeline, prefix: :base, allow_nil: true delegate :license_management_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
# sast_container_artifact is deprecated and replaced with container_scanning_artifact (#5778)
delegate :sast_container_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :sast_container_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :container_scanning_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :container_scanning_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :dast_artifact, to: :head_pipeline, prefix: :head, allow_nil: true
delegate :dast_artifact, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :sha, to: :head_pipeline, prefix: :head_pipeline, allow_nil: true delegate :sha, to: :head_pipeline, prefix: :head_pipeline, allow_nil: true
delegate :sha, to: :base_pipeline, prefix: :base_pipeline, allow_nil: true delegate :sha, to: :base_pipeline, prefix: :base_pipeline, allow_nil: true
delegate :has_sast_data?, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :has_dependency_scanning_data?, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :has_license_management_data?, to: :base_pipeline, prefix: :base, allow_nil: true delegate :has_license_management_data?, to: :base_pipeline, prefix: :base, allow_nil: true
# has_sast_container_data? is deprecated and replaced with has_container_scanning_data? (#5778)
delegate :has_sast_container_data?, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :has_container_scanning_data?, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :has_dast_data?, to: :base_pipeline, prefix: :base, allow_nil: true
delegate :expose_sast_data?, to: :head_pipeline, allow_nil: true
delegate :expose_dependency_scanning_data?, to: :head_pipeline, allow_nil: true
delegate :expose_license_management_data?, to: :head_pipeline, allow_nil: true delegate :expose_license_management_data?, to: :head_pipeline, allow_nil: true
# expose_sast_container_data? is deprecated and replaced with expose_container_scanning_data? (#5778)
delegate :expose_sast_container_data?, to: :head_pipeline, allow_nil: true
delegate :expose_container_scanning_data?, to: :head_pipeline, allow_nil: true
delegate :expose_dast_data?, to: :head_pipeline, allow_nil: true
delegate :merge_requests_author_approval?, to: :target_project, allow_nil: true delegate :merge_requests_author_approval?, to: :target_project, allow_nil: true
participant :participant_approvers participant :participant_approvers
......
...@@ -6,10 +6,17 @@ module EE ...@@ -6,10 +6,17 @@ module EE
size_limit_exceeded: 'Pipeline size limit exceeded!' size_limit_exceeded: 'Pipeline size limit exceeded!'
}.freeze }.freeze
def downloadable_url_for_report_type(file_type) def expose_security_dashboard?
if (job_artifact = artifact_for_file_type(file_type)) && any_report_artifact_for_type(:sast) ||
any_report_artifact_for_type(:dependency_scanning) ||
any_report_artifact_for_type(:dast) ||
any_report_artifact_for_type(:container_scanning)
end
def downloadable_path_for_report_type(file_type)
if (job_artifact = report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, job_artifact.job) can?(current_user, :read_build, job_artifact.job)
return download_project_build_artifacts_url( return download_project_job_artifacts_path(
job_artifact.project, job_artifact.project,
job_artifact.job, job_artifact.job,
file_type: file_type) file_type: file_type)
...@@ -17,7 +24,7 @@ module EE ...@@ -17,7 +24,7 @@ module EE
if (build_artifact = legacy_report_artifact_for_file_type(file_type)) && if (build_artifact = legacy_report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, build_artifact.build) can?(current_user, :read_build, build_artifact.build)
return raw_project_build_artifacts_url( return raw_project_job_artifacts_path(
build_artifact.build.project, build_artifact.build.project,
build_artifact.build, build_artifact.build,
path: build_artifact.path) path: build_artifact.path)
......
...@@ -16,13 +16,13 @@ module EE ...@@ -16,13 +16,13 @@ module EE
end end
end end
expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_url_for_report_type(:codequality) } do expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do
expose :head_path do |merge_request| expose :head_path do |merge_request|
head_pipeline_downloadable_url_for_report_type(:codequality) head_pipeline_downloadable_path_for_report_type(:codequality)
end end
expose :base_path do |merge_request| expose :base_path do |merge_request|
base_pipeline_downloadable_url_for_report_type(:codequality) base_pipeline_downloadable_path_for_report_type(:codequality)
end end
end end
...@@ -40,31 +40,23 @@ module EE ...@@ -40,31 +40,23 @@ module EE
end end
end end
expose :sast, if: -> (mr, _) { mr.expose_sast_data? } do expose :sast, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:sast) } do
expose :head_path, if: -> (mr, _) { can?(current_user, :read_build, mr.head_sast_artifact) } do |merge_request| expose :head_path do |merge_request|
raw_project_build_artifacts_url(merge_request.source_project, head_pipeline_downloadable_path_for_report_type(:sast)
merge_request.head_sast_artifact,
path: Ci::Build::SAST_FILE)
end end
expose :base_path, if: -> (mr, _) { mr.base_has_sast_data? && can?(current_user, :read_build, mr.base_sast_artifact) } do |merge_request| expose :base_path do |merge_request|
raw_project_build_artifacts_url(merge_request.target_project, base_pipeline_downloadable_path_for_report_type(:sast)
merge_request.base_sast_artifact,
path: Ci::Build::SAST_FILE)
end end
end end
expose :dependency_scanning, if: -> (mr, _) { mr.expose_dependency_scanning_data? } do expose :dependency_scanning, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:dependency_scanning) } do
expose :head_path, if: -> (mr, _) { can?(current_user, :read_build, mr.head_dependency_scanning_artifact) } do |merge_request| expose :head_path do |merge_request|
raw_project_build_artifacts_url(merge_request.source_project, head_pipeline_downloadable_path_for_report_type(:dependency_scanning)
merge_request.head_dependency_scanning_artifact,
path: Ci::Build::DEPENDENCY_SCANNING_FILE)
end end
expose :base_path, if: -> (mr, _) { mr.base_has_dependency_scanning_data? && can?(current_user, :read_build, mr.base_dependency_scanning_artifact) } do |merge_request| expose :base_path do |merge_request|
raw_project_build_artifacts_url(merge_request.target_project, base_pipeline_downloadable_path_for_report_type(:dependency_scanning)
merge_request.base_dependency_scanning_artifact,
path: Ci::Build::DEPENDENCY_SCANNING_FILE)
end end
end end
...@@ -98,47 +90,23 @@ module EE ...@@ -98,47 +90,23 @@ module EE
end end
end end
# expose_sast_container_data? is deprecated and replaced with expose_container_scanning_data? (#5778) expose :sast_container, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:container_scanning) } do
expose :sast_container, if: -> (mr, _) { mr.expose_sast_container_data? } do expose :head_path do |merge_request|
expose :head_path, if: -> (mr, _) { can?(current_user, :read_build, mr.head_sast_container_artifact) } do |merge_request| head_pipeline_downloadable_path_for_report_type(:container_scanning)
raw_project_build_artifacts_url(merge_request.source_project,
merge_request.head_sast_container_artifact,
path: Ci::Build::SAST_CONTAINER_FILE)
end
expose :base_path, if: -> (mr, _) { mr.base_has_sast_container_data? && can?(current_user, :read_build, mr.base_sast_container_artifact) } do |merge_request|
raw_project_build_artifacts_url(merge_request.target_project,
merge_request.base_sast_container_artifact,
path: Ci::Build::SAST_CONTAINER_FILE)
end
end
# We still expose it as `sast_container` to keep compatibility with Frontend (#5778)
expose :sast_container, if: -> (mr, _) { mr.expose_container_scanning_data? } do
expose :head_path, if: -> (mr, _) { can?(current_user, :read_build, mr.head_container_scanning_artifact) } do |merge_request|
raw_project_build_artifacts_url(merge_request.source_project,
merge_request.head_container_scanning_artifact,
path: Ci::Build::CONTAINER_SCANNING_FILE)
end end
expose :base_path, if: -> (mr, _) { mr.base_has_container_scanning_data? && can?(current_user, :read_build, mr.base_container_scanning_artifact) } do |merge_request| expose :base_path do |merge_request|
raw_project_build_artifacts_url(merge_request.target_project, base_pipeline_downloadable_path_for_report_type(:container_scanning)
merge_request.base_container_scanning_artifact,
path: Ci::Build::CONTAINER_SCANNING_FILE)
end end
end end
expose :dast, if: -> (mr, _) { mr.expose_dast_data? } do expose :dast, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:dast) } do
expose :head_path, if: -> (mr, _) { can?(current_user, :read_build, mr.head_dast_artifact) } do |merge_request| expose :head_path do |merge_request|
raw_project_build_artifacts_url(merge_request.source_project, head_pipeline_downloadable_path_for_report_type(:dast)
merge_request.head_dast_artifact,
path: Ci::Build::DAST_FILE)
end end
expose :base_path, if: -> (mr, _) { mr.base_has_dast_data? && can?(current_user, :read_build, mr.base_dast_artifact) } do |merge_request| expose :base_path do |merge_request|
raw_project_build_artifacts_url(merge_request.target_project, base_pipeline_downloadable_path_for_report_type(:dast)
merge_request.base_dast_artifact,
path: Ci::Build::DAST_FILE)
end end
end end
...@@ -170,12 +138,14 @@ module EE ...@@ -170,12 +138,14 @@ module EE
private private
def head_pipeline_downloadable_url_for_report_type(file_type) def head_pipeline_downloadable_path_for_report_type(file_type)
object.head_pipeline&.present(current_user: current_user)&.downloadable_url_for_report_type(file_type) object.head_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end end
def base_pipeline_downloadable_url_for_report_type(file_type) def base_pipeline_downloadable_path_for_report_type(file_type)
object.base_pipeline&.present(current_user: current_user)&.downloadable_url_for_report_type(file_type) object.base_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end end
end end
end end
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
class VulnerabilitySummaryEntity < Grape::Entity class VulnerabilitySummaryEntity < Grape::Entity
Vulnerabilities::Occurrence::REPORT_TYPES.each do |report_type_name, report_type| Vulnerabilities::Occurrence::REPORT_TYPES.each do |report_type_name, report_type|
report_key = Gitlab.rails5? ? report_type_name : report_type
expose report_type_name do expose report_type_name do
Vulnerabilities::Occurrence::LEVELS.each do |severity_name, severity| Vulnerabilities::Occurrence::LEVELS.each do |severity_name, severity|
expose severity_name do |group| expose severity_name do |group|
grouped_vulnerabilities[[report_type, severity]] || 0 grouped_vulnerabilities[[report_key, severity]] || 0
end end
end end
end end
......
...@@ -13,6 +13,7 @@ module Epics ...@@ -13,6 +13,7 @@ module Epics
def close_epic(epic) def close_epic(epic)
if epic.close if epic.close
epic.update(closed_by: current_user) epic.update(closed_by: current_user)
SystemNoteService.change_status(epic, nil, current_user, epic.state)
end end
end end
end end
......
...@@ -5,8 +5,15 @@ module Epics ...@@ -5,8 +5,15 @@ module Epics
def execute(epic) def execute(epic)
return epic unless can?(current_user, :update_epic, epic) return epic unless can?(current_user, :update_epic, epic)
epic.reopen reopen_epic(epic)
epic end
private
def reopen_epic(epic)
if epic.reopen
SystemNoteService.change_status(epic, nil, current_user, epic.state)
end
end end
end end
end end
- return unless can?(current_user, :admin_group, group) && License.feature_available?(:member_lock) - return unless can?(current_user, :admin_group, group) && License.feature_available?(:member_lock)
%hr %h5= _('Member lock')
.form-group.row .form-group
= f.label :membership_lock, class: 'col-form-label col-sm-2' do
Member lock
.col-sm-10
.form-check .form-check
= f.check_box :membership_lock, class: 'form-check-input' = f.check_box :membership_lock, class: 'form-check-input'
%span.descr Prevent adding new members to project membership within this group = f.label :membership_lock, class: 'form-check-label' do
%span= _('Prevent adding new members to project membership within this group')
- return unless group.feature_available?(:project_creation_level)
- form = local_assigns.fetch(:form)
- group = local_assigns.fetch(:group)
.form-group.row
= form.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'col-form-label col-sm-2'
.col-sm-10
= form.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, group.project_creation_level), {}, class: 'form-control'
- return unless group.feature_available?(:project_creation_level) - return unless group.feature_available?(:project_creation_level)
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
- group = local_assigns.fetch(:group) - group = local_assigns.fetch(:group)
.form-group.row
= form.label s_('ProjectCreationLevel|Allowed to create projects'), class: 'col-form-label col-sm-2' .form-group.col-md-9.row.prepend-top-8
.col-sm-10 = form.label :description, s_('ProjectCreationLevel|Allowed to create projects'), class: 'label-bold'
= form.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, group.project_creation_level), {}, class: 'form-control' = form.select :project_creation_level, options_for_select(::Gitlab::Access.project_creation_options, group.project_creation_level), {}, class: 'form-control'
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%section.settings.no-animate.expanded.cluster-health-graphs#cluster-health %section.settings.no-animate.expanded.cluster-health-graphs#cluster-health
%h4= s_('ClusterIntegration|Kubernetes cluster health') %h4= s_('ClusterIntegration|Kubernetes cluster health')
- if @cluster&.application_prometheus&.installed? - if @cluster&.application_prometheus_available?
#prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
"clusters-path": project_clusters_path(@project), "clusters-path": project_clusters_path(@project),
"documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'),
......
- pipeline = local_assigns.fetch(:pipeline) - pipeline = local_assigns.fetch(:pipeline)
- project = local_assigns.fetch(:project) - project = local_assigns.fetch(:project)
- sast_endpoint = pipeline.expose_sast_data? ? sast_artifact_url(pipeline) : nil - sast_endpoint = pipeline.downloadable_path_for_report_type(:sast)
- dependency_scanning_endpoint = pipeline.expose_dependency_scanning_data? ? dependency_scanning_artifact_url(pipeline) : nil - dependency_scanning_endpoint = pipeline.downloadable_path_for_report_type(:dependency_scanning)
- dast_endpoint = pipeline.expose_dast_data? ? dast_artifact_url(pipeline) : nil - dast_endpoint = pipeline.downloadable_path_for_report_type(:dast)
- sast_container_endpoint = pipeline.expose_sast_container_data? ? sast_container_artifact_url(pipeline) : pipeline.expose_container_scanning_data? ? container_scanning_artifact_url(pipeline) : nil - sast_container_endpoint = pipeline.downloadable_path_for_report_type(:container_scanning)
- blob_path = project_blob_path(project, pipeline.sha) - blob_path = project_blob_path(project, pipeline.sha)
- license_management_settings_path = can?(current_user, :admin_software_license_policy, project) ? license_management_settings_path(project) : nil - license_management_settings_path = can?(current_user, :admin_software_license_policy, project) ? license_management_settings_path(project) : nil
......
- return unless current_user.admin? && License.feature_available?(:repository_size_limit)
- form = local_assigns.fetch(:form)
- type = local_assigns.fetch(:type)
- label_class = (type == :project) ? 'label-bold' : 'col-form-label col-sm-2'
.form-group.row
= form.label :repository_size_limit, class: label_class do
Repository size limit (MB)
- if type == :project
= form.number_field :repository_size_limit, value: form.object.repository_size_limit.try(:to_mb), class: 'form-control', min: 0
%span.form-text.text-muted#repository_size_limit_help_block
= size_limit_message(@project)
- elsif type == :group
.col-sm-10
= form.number_field :repository_size_limit, value: form.object.repository_size_limit.try(:to_mb), class: 'form-control', min: 0
%span.form-text.text-muted#repository_size_limit_help_block
= size_limit_message_for_group(@group)
...@@ -2,17 +2,11 @@ ...@@ -2,17 +2,11 @@
- form = local_assigns.fetch(:form) - form = local_assigns.fetch(:form)
- type = local_assigns.fetch(:type) - type = local_assigns.fetch(:type)
- label_class = (type == :project) ? 'label-bold' : 'col-form-label col-sm-2' - form_group_class = type === :group ? 'col-md-9' : ''
.form-group.row .form-group{ class: form_group_class }
= form.label :repository_size_limit, class: label_class do = form.label :repository_size_limit, class: 'label-bold' do
Repository size limit (MB) Repository size limit (MB)
- if type == :project
= form.number_field :repository_size_limit, value: form.object.repository_size_limit.try(:to_mb), class: 'form-control', min: 0 = form.number_field :repository_size_limit, value: form.object.repository_size_limit.try(:to_mb), class: 'form-control', min: 0
%span.form-text.text-muted#repository_size_limit_help_block %span.form-text.text-muted#repository_size_limit_help_block
= size_limit_message(@project) = type === :project ? size_limit_message(@project) : size_limit_message_for_group(@group)
- elsif type == :group
.col-sm-10
= form.number_field :repository_size_limit, value: form.object.repository_size_limit.try(:to_mb), class: 'form-control', min: 0
%span.form-text.text-muted#repository_size_limit_help_block
= size_limit_message_for_group(@group)
---
title: Create system notes for epic close and reopen
merge_request: 7850
author:
type: added
---
title: 'Rails5: fix VulnerabilitySummaryEntity'
merge_request: 7893
author: Jasper Maes
type: other
---
title: Refactor test reports to use new artifact architecture.
merge_request: 7827
author:
type: changed
...@@ -23,7 +23,7 @@ describe Projects::PipelinesController do ...@@ -23,7 +23,7 @@ describe Projects::PipelinesController do
pipeline: pipeline, pipeline: pipeline,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
......
...@@ -23,7 +23,7 @@ describe Projects::Security::DashboardController do ...@@ -23,7 +23,7 @@ describe Projects::Security::DashboardController do
pipeline: pipeline_1, pipeline: pipeline_1,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
......
...@@ -26,7 +26,7 @@ describe 'Pipeline', :js do ...@@ -26,7 +26,7 @@ describe 'Pipeline', :js do
pipeline: pipeline, pipeline: pipeline,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
......
...@@ -120,30 +120,9 @@ describe Ci::Build do ...@@ -120,30 +120,9 @@ describe Ci::Build do
filename: Ci::Build::PERFORMANCE_FILE, filename: Ci::Build::PERFORMANCE_FILE,
job_names: %w[performance deploy] job_names: %w[performance deploy]
}, },
has_sast_json?: {
filename: Ci::Build::SAST_FILE,
job_names: %w[sast]
},
has_dependency_scanning_json?: {
filename: Ci::Build::DEPENDENCY_SCANNING_FILE,
job_names: %w[dependency_scanning]
},
has_license_management_json?: { has_license_management_json?: {
filename: Ci::Build::LICENSE_MANAGEMENT_FILE, filename: Ci::Build::LICENSE_MANAGEMENT_FILE,
job_names: %w[license_management] job_names: %w[license_management]
},
# has_sast_container_json? is deprecated and replaced with has_container_scanning_json (#5778)
has_sast_container_json?: {
filename: Ci::Build::SAST_CONTAINER_FILE,
job_names: %w[sast:container container_scanning]
},
has_container_scanning_json?: {
filename: Ci::Build::CONTAINER_SCANNING_FILE,
job_names: %w[sast:container container_scanning]
},
has_dast_json?: {
filename: Ci::Build::DAST_FILE,
job_names: %w[dast]
} }
} }
......
...@@ -20,15 +20,7 @@ describe Ci::Pipeline do ...@@ -20,15 +20,7 @@ describe Ci::Pipeline do
PIPELINE_ARTIFACTS_METHODS = [ PIPELINE_ARTIFACTS_METHODS = [
{ method: :performance_artifact, options: [Ci::Build::PERFORMANCE_FILE, 'performance'] }, { method: :performance_artifact, options: [Ci::Build::PERFORMANCE_FILE, 'performance'] },
{ method: :sast_artifact, options: [Ci::Build::SAST_FILE, 'sast'] }, { method: :license_management_artifact, options: [Ci::Build::LICENSE_MANAGEMENT_FILE, 'license_management'] }
{ method: :dependency_scanning_artifact, options: [Ci::Build::DEPENDENCY_SCANNING_FILE, 'dependency_scanning'] },
{ method: :license_management_artifact, options: [Ci::Build::LICENSE_MANAGEMENT_FILE, 'license_management'] },
# sast_container_artifact is deprecated and replaced with container_scanning_artifact (#5778)
{ method: :sast_container_artifact, options: [Ci::Build::SAST_CONTAINER_FILE, 'sast:container'] },
{ method: :sast_container_artifact, options: [Ci::Build::SAST_CONTAINER_FILE, 'container_scanning'] },
{ method: :container_scanning_artifact, options: [Ci::Build::CONTAINER_SCANNING_FILE, 'sast:container'] },
{ method: :container_scanning_artifact, options: [Ci::Build::CONTAINER_SCANNING_FILE, 'container_scanning'] },
{ method: :dast_artifact, options: [Ci::Build::DAST_FILE, 'dast'] }
].freeze ].freeze
PIPELINE_ARTIFACTS_METHODS.each do |method_test| PIPELINE_ARTIFACTS_METHODS.each do |method_test|
...@@ -64,7 +56,7 @@ describe Ci::Pipeline do ...@@ -64,7 +56,7 @@ describe Ci::Pipeline do
end end
end end
%w(sast dependency_scanning dast performance sast_container container_scanning).each do |type| %w(performance license_management).each do |type|
method = "has_#{type}_data?" method = "has_#{type}_data?"
describe "##{method}" do describe "##{method}" do
...@@ -78,7 +70,7 @@ describe Ci::Pipeline do ...@@ -78,7 +70,7 @@ describe Ci::Pipeline do
end end
end end
%w(sast dependency_scanning dast performance sast_container container_scanning).each do |type| %w(performance license_management).each do |type|
method = "expose_#{type}_data?" method = "expose_#{type}_data?"
describe "##{method}" do describe "##{method}" do
...@@ -107,7 +99,7 @@ describe Ci::Pipeline do ...@@ -107,7 +99,7 @@ describe Ci::Pipeline do
pipeline: pipeline_1, pipeline: pipeline_1,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
...@@ -119,7 +111,7 @@ describe Ci::Pipeline do ...@@ -119,7 +111,7 @@ describe Ci::Pipeline do
pipeline: pipeline_2, pipeline: pipeline_2,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::DEPENDENCY_SCANNING_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:dependency_scanning]]
} }
} }
) )
...@@ -131,7 +123,7 @@ describe Ci::Pipeline do ...@@ -131,7 +123,7 @@ describe Ci::Pipeline do
pipeline: pipeline_3, pipeline: pipeline_3,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::CONTAINER_SCANNING_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:container_scanning]]
} }
} }
) )
...@@ -143,7 +135,7 @@ describe Ci::Pipeline do ...@@ -143,7 +135,7 @@ describe Ci::Pipeline do
pipeline: pipeline_4, pipeline: pipeline_4,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::DAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:dast]]
} }
} }
) )
...@@ -152,12 +144,7 @@ describe Ci::Pipeline do ...@@ -152,12 +144,7 @@ describe Ci::Pipeline do
:success, :success,
:artifacts, :artifacts,
name: 'foobar', name: 'foobar',
pipeline: pipeline_5, pipeline: pipeline_5
options: {
artifacts: {
paths: ['foobar-report.json']
}
}
) )
end end
...@@ -166,12 +153,12 @@ describe Ci::Pipeline do ...@@ -166,12 +153,12 @@ describe Ci::Pipeline do
end end
end end
describe '#artifact_for_file_type' do describe '#report_artifact_for_file_type' do
let(:file_type) { :codequality } let(:file_type) { :codequality }
let!(:build) { create(:ci_build, pipeline: pipeline) } let!(:build) { create(:ci_build, pipeline: pipeline) }
let!(:artifact) { create(:ci_job_artifact, :codequality, job: build) } let!(:artifact) { create(:ci_job_artifact, :codequality, job: build) }
subject { pipeline.artifact_for_file_type(file_type) } subject { pipeline.report_artifact_for_file_type(file_type) }
it 'returns the artifact' do it 'returns the artifact' do
expect(subject).to eq(artifact) expect(subject).to eq(artifact)
...@@ -221,12 +208,12 @@ describe Ci::Pipeline do ...@@ -221,12 +208,12 @@ describe Ci::Pipeline do
end end
it 'does not perform extra queries when calling pipeline artifacts methods after the first' do it 'does not perform extra queries when calling pipeline artifacts methods after the first' do
create_build('sast', Ci::Build::SAST_FILE) create_build('performance', 'performance.json')
create_build('dependency_scanning', 'gl-dependency-scanning-report.json') create_build('license_management', 'gl-license-management-report.json')
pipeline.sast_artifact pipeline.performance_artifact
expect { pipeline.dependency_scanning_artifact }.not_to exceed_query_limit(0) expect { pipeline.license_management_artifact }.not_to exceed_query_limit(0)
end end
end end
end end
...@@ -63,11 +63,26 @@ describe MergeRequest do ...@@ -63,11 +63,26 @@ describe MergeRequest do
end end
end end
%w(sast dast sast_container container_scanning).each do |type| describe '#base_license_management_artifact' do
it { is_expected.to delegate_method(:"expose_#{type}_data?").to(:head_pipeline) } before do
it { is_expected.to delegate_method(:"has_#{type}_data?").to(:base_pipeline).with_prefix(:base) } allow(subject.base_pipeline).to receive(:license_management_artifact)
it { is_expected.to delegate_method(:"#{type}_artifact").to(:head_pipeline).with_prefix(:head) } .and_return(1)
it { is_expected.to delegate_method(:"#{type}_artifact").to(:base_pipeline).with_prefix(:base) } end
it 'delegates to merge request diff' do
expect(subject.base_license_management_artifact).to eq(1)
end
end
describe '#head_license_management_artifact' do
before do
allow(subject.head_pipeline).to receive(:license_management_artifact)
.and_return(1)
end
it 'delegates to merge request diff' do
expect(subject.head_license_management_artifact).to eq(1)
end
end end
describe '#expose_performance_data?' do describe '#expose_performance_data?' do
...@@ -86,4 +101,15 @@ describe MergeRequest do ...@@ -86,4 +101,15 @@ describe MergeRequest do
it { expect(subject.expose_performance_data?).to be_falsey } it { expect(subject.expose_performance_data?).to be_falsey }
end end
end end
describe '#expose_license_management_data?' do
before do
allow(subject.head_pipeline).to receive(:expose_license_management_data?)
.and_return(1)
end
it 'delegates to merge request diff' do
expect(subject.expose_license_management_data?).to eq(1)
end
end
end end
...@@ -1541,7 +1541,7 @@ describe Project do ...@@ -1541,7 +1541,7 @@ describe Project do
pipeline: pipeline_1, pipeline: pipeline_1,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
...@@ -1553,7 +1553,7 @@ describe Project do ...@@ -1553,7 +1553,7 @@ describe Project do
pipeline: pipeline_2, pipeline: pipeline_2,
options: { options: {
artifacts: { artifacts: {
paths: [Ci::Build::SAST_FILE] paths: [Ci::JobArtifact::DEFAULT_FILE_NAMES[:sast]]
} }
} }
) )
......
...@@ -26,7 +26,22 @@ describe MergeRequestWidgetEntity do ...@@ -26,7 +26,22 @@ describe MergeRequestWidgetEntity do
expect(subject.as_json[:blob_path]).to include(:head_path) expect(subject.as_json[:blob_path]).to include(:head_path)
end end
describe 'codeclimate' do it 'sets approvals_before_merge to 0 if nil' do
expect(subject.as_json[:approvals_before_merge]).to eq(0)
end
describe 'test report artifacts' do
using RSpec::Parameterized::TableSyntax
where(:json_entry, :artifact_type) do
:codeclimate | :codequality
:sast | :sast
:dependency_scanning | :dependency_scanning
:sast_container | :container_scanning
:dast | :dast
end
with_them do
before do before do
allow(merge_request).to receive_messages( allow(merge_request).to receive_messages(
base_pipeline: pipeline, base_pipeline: pipeline,
...@@ -34,26 +49,29 @@ describe MergeRequestWidgetEntity do ...@@ -34,26 +49,29 @@ describe MergeRequestWidgetEntity do
) )
end end
context 'with codeclimate data' do context 'when feature is available' do
before do
allow(pipeline).to receive(:available_licensed_report_type?).and_return(true)
end
context "with data" do
before do before do
job = create(:ci_build, pipeline: pipeline) job = create(:ci_build, pipeline: pipeline)
create(:ci_job_artifact, :codequality, job: job) create(:ci_job_artifact, file_type: artifact_type, file_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[artifact_type], job: job)
end end
it 'has codeclimate data entry' do it "has data entry" do
expect(subject.as_json).to include(:codeclimate) expect(subject.as_json).to include(json_entry)
end end
end end
context 'without codeclimate data' do context "without data" do
it 'does not have codeclimate data entry' do it "does not have data entry" do
expect(subject.as_json).not_to include(:codeclimate) expect(subject.as_json).not_to include(json_entry)
end
end end
end end
end end
it 'sets approvals_before_merge to 0 if nil' do
expect(subject.as_json[:approvals_before_merge]).to eq(0)
end end
it 'has performance data' do it 'has performance data' do
...@@ -61,7 +79,6 @@ describe MergeRequestWidgetEntity do ...@@ -61,7 +79,6 @@ describe MergeRequestWidgetEntity do
allow(merge_request).to receive_messages( allow(merge_request).to receive_messages(
expose_performance_data?: true, expose_performance_data?: true,
expose_security_dashboard?: false,
base_performance_artifact: build, base_performance_artifact: build,
head_performance_artifact: build head_performance_artifact: build
) )
...@@ -69,45 +86,12 @@ describe MergeRequestWidgetEntity do ...@@ -69,45 +86,12 @@ describe MergeRequestWidgetEntity do
expect(subject.as_json).to include(:performance) expect(subject.as_json).to include(:performance)
end end
it 'has sast data' do
build = create(:ci_build, name: 'sast', pipeline: pipeline)
allow(merge_request).to receive_messages(
expose_sast_data?: true,
expose_security_dashboard?: true,
base_has_sast_data?: true,
base_sast_artifact: build,
head_sast_artifact: build
)
expect(subject.as_json).to include(:sast)
expect(subject.as_json[:sast]).to include(:head_path)
expect(subject.as_json[:sast]).to include(:base_path)
end
it 'has dependency_scanning data' do
build = create(:ci_build, name: 'dependency_scanning', pipeline: pipeline)
allow(merge_request).to receive_messages(
expose_dependency_scanning_data?: true,
expose_security_dashboard?: true,
base_has_dependency_scanning_data?: true,
base_dependency_scanning_artifact: build,
head_dependency_scanning_artifact: build
)
expect(subject.as_json).to include(:dependency_scanning)
expect(subject.as_json[:dependency_scanning]).to include(:head_path)
expect(subject.as_json[:dependency_scanning]).to include(:base_path)
end
describe '#license_management' do describe '#license_management' do
before do before do
build = create(:ci_build, name: 'license_management', pipeline: pipeline) build = create(:ci_build, name: 'license_management', pipeline: pipeline)
allow(merge_request).to receive_messages( allow(merge_request).to receive_messages(
expose_license_management_data?: true, expose_license_management_data?: true,
expose_security_dashboard?: false,
base_has_license_management_data?: true, base_has_license_management_data?: true,
base_license_management_artifact: build, base_license_management_artifact: build,
head_license_management_artifact: build, head_license_management_artifact: build,
...@@ -143,55 +127,6 @@ describe MergeRequestWidgetEntity do ...@@ -143,55 +127,6 @@ describe MergeRequestWidgetEntity do
end end
end end
# methods for old artifact are deprecated and replaced with ones for the new name (#5779)
it 'has sast_container data (with old artifact name gl-sast-container-report.json)' do
build = create(:ci_build, name: 'container_scanning', pipeline: pipeline)
allow(merge_request).to receive_messages(
expose_sast_container_data?: true,
expose_security_dashboard?: true,
base_has_sast_container_data?: true,
base_sast_container_artifact: build,
head_sast_container_artifact: build
)
expect(subject.as_json).to include(:sast_container)
expect(subject.as_json[:sast_container]).to include(:head_path)
expect(subject.as_json[:sast_container]).to include(:base_path)
end
it 'has sast_container data (with new artifact name gl-container-scanning-report.json)' do
build = create(:ci_build, name: 'container_scanning', pipeline: pipeline)
allow(merge_request).to receive_messages(
expose_container_scanning_data?: true,
expose_security_dashboard?: true,
base_has_container_scanning_data?: true,
base_container_scanning_artifact: build,
head_container_scanning_artifact: build
)
expect(subject.as_json).to include(:sast_container)
expect(subject.as_json[:sast_container]).to include(:head_path)
expect(subject.as_json[:sast_container]).to include(:base_path)
end
it 'has dast data' do
build = create(:ci_build, name: 'dast', pipeline: pipeline)
allow(merge_request).to receive_messages(
expose_dast_data?: true,
expose_security_dashboard?: true,
base_has_dast_data?: true,
base_dast_artifact: build,
head_dast_artifact: build
)
expect(subject.as_json).to include(:dast)
expect(subject.as_json[:dast]).to include(:head_path)
expect(subject.as_json[:dast]).to include(:base_path)
end
it 'has vulnerability feedbacks path' do it 'has vulnerability feedbacks path' do
expect(subject.as_json).to include(:vulnerability_feedback_path) expect(subject.as_json).to include(:vulnerability_feedback_path)
end end
......
...@@ -41,6 +41,15 @@ describe Epics::CloseService do ...@@ -41,6 +41,15 @@ describe Epics::CloseService do
it 'changes closed_at' do it 'changes closed_at' do
expect { subject.execute(epic) }.to change { epic.closed_at } expect { subject.execute(epic) }.to change { epic.closed_at }
end end
it 'creates a system note about epic close' do
expect { subject.execute(epic) }.to change { epic.notes.count }.by(1)
note = epic.notes.last
expect(note.note).to eq('closed')
expect(note.system_note_metadata.action).to eq('closed')
end
end end
context 'when trying to close a closed epic' do context 'when trying to close a closed epic' do
...@@ -59,6 +68,10 @@ describe Epics::CloseService do ...@@ -59,6 +68,10 @@ describe Epics::CloseService do
it 'does not change closed_by' do it 'does not change closed_by' do
expect { subject.execute(epic) }.not_to change { epic.closed_by } expect { subject.execute(epic) }.not_to change { epic.closed_by }
end end
it 'does not create a system note' do
expect { subject.execute(epic) }.not_to change { epic.notes.count }
end
end end
end end
......
...@@ -41,6 +41,15 @@ describe Epics::ReopenService do ...@@ -41,6 +41,15 @@ describe Epics::ReopenService do
it 'removes closed_at' do it 'removes closed_at' do
expect { subject.execute(epic) }.to change { epic.closed_at }.to(nil) expect { subject.execute(epic) }.to change { epic.closed_at }.to(nil)
end end
it 'creates a system note about epic reopen' do
expect { subject.execute(epic) }.to change { epic.notes.count }.by(1)
note = epic.notes.last
expect(note.note).to eq('opened')
expect(note.system_note_metadata.action).to eq('opened')
end
end end
context 'when trying to reopen an opened epic' do context 'when trying to reopen an opened epic' do
...@@ -59,6 +68,10 @@ describe Epics::ReopenService do ...@@ -59,6 +68,10 @@ describe Epics::ReopenService do
it 'does not change closed_by' do it 'does not change closed_by' do
expect { subject.execute(epic) }.not_to change { epic.closed_by } expect { subject.execute(epic) }.not_to change { epic.closed_by }
end end
it 'does not create a system note' do
expect { subject.execute(epic) }.not_to change { epic.notes.count }
end
end end
end end
......
...@@ -4820,6 +4820,9 @@ msgstr "" ...@@ -4820,6 +4820,9 @@ msgstr ""
msgid "Median" msgid "Median"
msgstr "" msgstr ""
msgid "Member lock"
msgstr ""
msgid "Member since %{date}" msgid "Member since %{date}"
msgstr "" msgstr ""
...@@ -5817,6 +5820,9 @@ msgstr "" ...@@ -5817,6 +5820,9 @@ msgstr ""
msgid "Press Enter or click to search" msgid "Press Enter or click to search"
msgstr "" msgstr ""
msgid "Prevent adding new members to project membership within this group"
msgstr ""
msgid "Preview" msgid "Preview"
msgstr "" msgstr ""
......
...@@ -54,12 +54,10 @@ describe('Environment table', () => { ...@@ -54,12 +54,10 @@ describe('Environment table', () => {
}); });
expect(vm.$el.querySelector('.js-deploy-board-row')).toBeDefined(); expect(vm.$el.querySelector('.js-deploy-board-row')).toBeDefined();
expect( expect(vm.$el.querySelector('.deploy-board-icon')).not.toBeNull();
vm.$el.querySelector('.deploy-board-icon i').classList.contains('fa-caret-right'),
).toEqual(true);
}); });
it('should toggle deploy board visibility when arrow is clicked', () => { it('should toggle deploy board visibility when arrow is clicked', (done) => {
const mockItem = { const mockItem = {
name: 'review', name: 'review',
size: 1, size: 1,
...@@ -80,6 +78,7 @@ describe('Environment table', () => { ...@@ -80,6 +78,7 @@ describe('Environment table', () => {
eventHub.$on('toggleDeployBoard', (env) => { eventHub.$on('toggleDeployBoard', (env) => {
expect(env.id).toEqual(mockItem.id); expect(env.id).toEqual(mockItem.id);
done();
}); });
vm = mountComponent(Component, { vm = mountComponent(Component, {
......
...@@ -113,9 +113,7 @@ describe('Environment', () => { ...@@ -113,9 +113,7 @@ describe('Environment', () => {
describe('deploy boards', () => { describe('deploy boards', () => {
it('should render arrow to open deploy boards', (done) => { it('should render arrow to open deploy boards', (done) => {
setTimeout(() => { setTimeout(() => {
expect( expect(component.$el.querySelector('.deploy-board-icon.ic-chevron-right')).toBeDefined();
component.$el.querySelector('.deploy-board-icon i.fa-caret-right'),
).toBeDefined();
done(); done();
}, 0); }, 0);
}); });
...@@ -169,12 +167,7 @@ describe('Environment', () => { ...@@ -169,12 +167,7 @@ describe('Environment', () => {
component.$el.querySelector('.folder-name').click(); component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => { Vue.nextTick(() => {
expect( expect(component.$el.querySelector('.folder-icon.ic-chevron-right')).toBe(null);
component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
).toContain('display: none');
expect(
component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
).not.toContain('display: none');
done(); done();
}); });
}, 0); }, 0);
...@@ -190,12 +183,7 @@ describe('Environment', () => { ...@@ -190,12 +183,7 @@ describe('Environment', () => {
component.$el.querySelector('.folder-name').click(); component.$el.querySelector('.folder-name').click();
Vue.nextTick(() => { Vue.nextTick(() => {
expect( expect(component.$el.querySelector('.folder-icon.ic-chevron-down')).toBe(null);
component.$el.querySelector('.folder-icon i.fa-caret-down').getAttribute('style'),
).toContain('display: none');
expect(
component.$el.querySelector('.folder-icon i.fa-caret-right').getAttribute('style'),
).not.toContain('display: none');
done(); done();
}); });
}); });
......
...@@ -125,9 +125,7 @@ describe('Environments Folder View', () => { ...@@ -125,9 +125,7 @@ describe('Environments Folder View', () => {
describe('deploy boards', () => { describe('deploy boards', () => {
it('should render arrow to open deploy boards', (done) => { it('should render arrow to open deploy boards', (done) => {
setTimeout(() => { setTimeout(() => {
expect( expect(component.$el.querySelector('.folder-icon.ic-chevron-right')).not.toBeNull();
component.$el.querySelector('.deploy-board-icon i').classList.contains('fa-caret-right'),
).toEqual(true);
done(); done();
}, 0); }, 0);
}); });
......
...@@ -4,7 +4,7 @@ describe Clusters::Applications::Ingress do ...@@ -4,7 +4,7 @@ describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress) } let(:ingress) { create(:clusters_applications_ingress) }
include_examples 'cluster application core specs', :clusters_applications_ingress include_examples 'cluster application core specs', :clusters_applications_ingress
include_examples 'cluster application status specs', :cluster_application_ingress include_examples 'cluster application status specs', :clusters_applications_ingress
before do before do
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in) allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
......
...@@ -4,7 +4,7 @@ describe Clusters::Applications::Prometheus do ...@@ -4,7 +4,7 @@ describe Clusters::Applications::Prometheus do
include KubernetesHelpers include KubernetesHelpers
include_examples 'cluster application core specs', :clusters_applications_prometheus include_examples 'cluster application core specs', :clusters_applications_prometheus
include_examples 'cluster application status specs', :cluster_application_prometheus include_examples 'cluster application status specs', :clusters_applications_prometheus
describe '.installed' do describe '.installed' do
subject { described_class.installed } subject { described_class.installed }
......
...@@ -4,7 +4,7 @@ describe Clusters::Applications::Runner do ...@@ -4,7 +4,7 @@ describe Clusters::Applications::Runner do
let(:ci_runner) { create(:ci_runner) } let(:ci_runner) { create(:ci_runner) }
include_examples 'cluster application core specs', :clusters_applications_runner include_examples 'cluster application core specs', :clusters_applications_runner
include_examples 'cluster application status specs', :cluster_application_runner include_examples 'cluster application status specs', :clusters_applications_runner
it { is_expected.to belong_to(:runner) } it { is_expected.to belong_to(:runner) }
......
# frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
describe Clusters::Cluster do describe Clusters::Cluster do
...@@ -15,8 +17,9 @@ describe Clusters::Cluster do ...@@ -15,8 +17,9 @@ describe Clusters::Cluster do
it { is_expected.to delegate_method(:on_creation?).to(:provider) } it { is_expected.to delegate_method(:on_creation?).to(:provider) }
it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix } it { is_expected.to delegate_method(:active?).to(:platform_kubernetes).with_prefix }
it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix } it { is_expected.to delegate_method(:rbac?).to(:platform_kubernetes).with_prefix }
it { is_expected.to delegate_method(:installed?).to(:application_helm).with_prefix } it { is_expected.to delegate_method(:available?).to(:application_helm).with_prefix }
it { is_expected.to delegate_method(:installed?).to(:application_ingress).with_prefix } it { is_expected.to delegate_method(:available?).to(:application_ingress).with_prefix }
it { is_expected.to delegate_method(:available?).to(:application_prometheus).with_prefix }
it { is_expected.to respond_to :project } it { is_expected.to respond_to :project }
describe '.enabled' do describe '.enabled' do
......
# frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
describe PrometheusService, :use_clean_rails_memory_store_caching do describe PrometheusService, :use_clean_rails_memory_store_caching do
...@@ -83,13 +85,22 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do ...@@ -83,13 +85,22 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end end
end end
describe '#prometheus_installed?' do describe '#prometheus_available?' do
context 'clusters with installed prometheus' do context 'clusters with installed prometheus' do
let!(:cluster) { create(:cluster, projects: [project]) } let!(:cluster) { create(:cluster, projects: [project]) }
let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
it 'returns true' do it 'returns true' do
expect(service.prometheus_installed?).to be(true) expect(service.prometheus_available?).to be(true)
end
end
context 'clusters with updated prometheus' do
let!(:cluster) { create(:cluster, projects: [project]) }
let!(:prometheus) { create(:clusters_applications_prometheus, :updated, cluster: cluster) }
it 'returns true' do
expect(service.prometheus_available?).to be(true)
end end
end end
...@@ -98,7 +109,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do ...@@ -98,7 +109,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) } let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) }
it 'returns false' do it 'returns false' do
expect(service.prometheus_installed?).to be(false) expect(service.prometheus_available?).to be(false)
end end
end end
...@@ -106,13 +117,13 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do ...@@ -106,13 +117,13 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
let(:cluster) { create(:cluster, projects: [project]) } let(:cluster) { create(:cluster, projects: [project]) }
it 'returns false' do it 'returns false' do
expect(service.prometheus_installed?).to be(false) expect(service.prometheus_available?).to be(false)
end end
end end
context 'no clusters' do context 'no clusters' do
it 'returns false' do it 'returns false' do
expect(service.prometheus_installed?).to be(false) expect(service.prometheus_available?).to be(false)
end end
end end
end end
...@@ -150,7 +161,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do ...@@ -150,7 +161,7 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with prometheus installed in the cluster' do context 'with prometheus installed in the cluster' do
before do before do
allow(service).to receive(:prometheus_installed?).and_return(true) allow(service).to receive(:prometheus_available?).and_return(true)
end end
context 'when service is inactive' do context 'when service is inactive' do
......
...@@ -11,60 +11,4 @@ shared_examples 'cluster application core specs' do |application_name| ...@@ -11,60 +11,4 @@ shared_examples 'cluster application core specs' do |application_name|
expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class) expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class)
end end
end end
describe 'status state machine' do
describe '#make_installing' do
subject { create(application_name, :scheduled) }
it 'is installing' do
subject.make_installing!
expect(subject).to be_installing
end
end
describe '#make_installed' do
subject { create(application_name, :installing) }
it 'is installed' do
subject.make_installed
expect(subject).to be_installed
end
end
describe '#make_errored' do
subject { create(application_name, :installing) }
let(:reason) { 'some errors' }
it 'is errored' do
subject.make_errored(reason)
expect(subject).to be_errored
expect(subject.status_reason).to eq(reason)
end
end
describe '#make_scheduled' do
subject { create(application_name, :installable) }
it 'is scheduled' do
subject.make_scheduled
expect(subject).to be_scheduled
end
describe 'when was errored' do
subject { create(application_name, :errored) }
it 'clears #status_reason' do
expect(subject.status_reason).not_to be_nil
subject.make_scheduled!
expect(subject.status_reason).to be_nil
end
end
end
end
end end
...@@ -28,4 +28,87 @@ shared_examples 'cluster application status specs' do |application_name| ...@@ -28,4 +28,87 @@ shared_examples 'cluster application status specs' do |application_name|
end end
end end
end end
describe 'status state machine' do
describe '#make_installing' do
subject { create(application_name, :scheduled) }
it 'is installing' do
subject.make_installing!
expect(subject).to be_installing
end
end
describe '#make_installed' do
subject { create(application_name, :installing) }
it 'is installed' do
subject.make_installed
expect(subject).to be_installed
end
end
describe '#make_errored' do
subject { create(application_name, :installing) }
let(:reason) { 'some errors' }
it 'is errored' do
subject.make_errored(reason)
expect(subject).to be_errored
expect(subject.status_reason).to eq(reason)
end
end
describe '#make_scheduled' do
subject { create(application_name, :installable) }
it 'is scheduled' do
subject.make_scheduled
expect(subject).to be_scheduled
end
describe 'when was errored' do
subject { create(application_name, :errored) }
it 'clears #status_reason' do
expect(subject.status_reason).not_to be_nil
subject.make_scheduled!
expect(subject.status_reason).to be_nil
end
end
end
end
describe '#available?' do
using RSpec::Parameterized::TableSyntax
where(:trait, :available) do
:not_installable | false
:installable | false
:scheduled | false
:installing | false
:installed | true
:updating | false
:updated | true
:errored | false
:update_errored | false
:timeouted | false
end
with_them do
subject { build(application_name, trait) }
if params[:available]
it { is_expected.to be_available }
else
it { is_expected.not_to be_available }
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment