Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a483e9271b
commit
a8caa9ce49
|
|
@ -2,14 +2,6 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 13.3.6 (2020-09-14)
|
||||
|
||||
### Fixed (2 changes)
|
||||
|
||||
- Fixes Auto DevOps deploy script for multiple additional hosts separated by comma and space. !41404
|
||||
- Use 'read' method to get request body in Conan to fix uploads when using Unicorn. !41801
|
||||
|
||||
|
||||
## 13.3.4 (2020-09-02)
|
||||
|
||||
### Security (1 change)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlDropdown, GlNewDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import allVersionsMixin from '../../mixins/all_versions';
|
||||
import { findVersionId } from '../../utils/design_management_utils';
|
||||
|
|
@ -7,7 +7,7 @@ import { findVersionId } from '../../utils/design_management_utils';
|
|||
export default {
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlSprintf,
|
||||
},
|
||||
mixins: [allVersionsMixin],
|
||||
|
|
@ -64,7 +64,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-dropdown :text="dropdownText" size="small">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="(version, index) in allVersions"
|
||||
:key="version.id"
|
||||
:is-check-item="true"
|
||||
|
|
@ -76,6 +76,6 @@ export default {
|
|||
{{ allVersions.length - index }}
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
GlFormGroup,
|
||||
GlFormCheckbox,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
} from '@gitlab/ui';
|
||||
import {
|
||||
I18N_ALERT_SETTINGS_FORM,
|
||||
|
|
@ -25,7 +25,7 @@ export default {
|
|||
GlIcon,
|
||||
GlFormCheckbox,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
},
|
||||
inject: ['service', 'alertSettings'],
|
||||
data() {
|
||||
|
|
@ -107,7 +107,7 @@ export default {
|
|||
:text="issueTemplateHeader"
|
||||
:block="true"
|
||||
>
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="template in templates"
|
||||
:key="template.key"
|
||||
data-qa-selector="incident_templates_item"
|
||||
|
|
@ -116,7 +116,7 @@ export default {
|
|||
@click="selectIssueTemplate(template.key)"
|
||||
>
|
||||
{{ template.name }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</gl-form-group>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { GlDropdown, GlNewDropdownItem, GlLink } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import { defaultIntegrationLevel, overrideDropdownDescriptions } from '../constants';
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export default {
|
|||
name: 'OverrideDropdown',
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlLink,
|
||||
},
|
||||
props: {
|
||||
|
|
@ -74,13 +74,13 @@ export default {
|
|||
</span>
|
||||
<input name="service[inherit_from_id]" :value="override ? '' : inheritFromId" type="hidden" />
|
||||
<gl-dropdown :text="selected.text">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="option in $options.dropdownOptions"
|
||||
:key="option.value"
|
||||
@click="onClick(option)"
|
||||
>
|
||||
{{ option.text }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlAlert,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlDropdownText,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
|
|
@ -35,7 +35,7 @@ export default {
|
|||
GlAlert,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlDropdownText,
|
||||
GlFormGroup,
|
||||
GlFormSelect,
|
||||
|
|
@ -305,14 +305,14 @@ export default {
|
|||
|
||||
<gl-loading-icon v-if="isFetching" />
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="user in users"
|
||||
v-else
|
||||
:key="user.id"
|
||||
@click="updateMapping(data.item.jiraAccountId, user.id, user.username)"
|
||||
>
|
||||
{{ user.username }} ({{ user.name }})
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-dropdown-text v-show="shouldShowNoMatchesFoundText" class="text-secondary">
|
||||
{{ __('No matches found') }}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import {
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlLoadingIcon,
|
||||
GlSearchBoxByType,
|
||||
GlIcon,
|
||||
|
|
@ -18,9 +18,9 @@ const SEARCH_DEBOUNCE_MS = 250;
|
|||
export default {
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlLoadingIcon,
|
||||
GlSearchBoxByType,
|
||||
GlIcon,
|
||||
|
|
@ -200,7 +200,7 @@ export default {
|
|||
<span class="text-center d-block">{{ $options.translations.selectMilestone }}</span>
|
||||
</gl-new-dropdown-header>
|
||||
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
|
||||
<gl-search-box-by-type
|
||||
ref="searchBox"
|
||||
|
|
@ -211,26 +211,26 @@ export default {
|
|||
@keydown.enter.prevent="onSearchBoxEnter"
|
||||
/>
|
||||
|
||||
<gl-new-dropdown-item @click="onMilestoneClicked(null)">
|
||||
<gl-dropdown-item @click="onMilestoneClicked(null)">
|
||||
<span :class="{ 'pl-4': true, 'selected-item': selectedMilestones.length === 0 }">
|
||||
{{ $options.translations.noMilestone }}
|
||||
</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
|
||||
<template v-if="isLoading">
|
||||
<gl-loading-icon />
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
</template>
|
||||
<template v-else-if="noResults">
|
||||
<div class="dropdown-item-space">
|
||||
<span ref="noResults" class="pl-4">{{ $options.translations.noResultsLabel }}</span>
|
||||
</div>
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
</template>
|
||||
<template v-else-if="dropdownItems.length">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="item in dropdownItems"
|
||||
:key="item"
|
||||
role="milestone option"
|
||||
|
|
@ -239,12 +239,12 @@ export default {
|
|||
<span :class="{ 'pl-4': true, 'selected-item': isSelectedMilestone(item) }">
|
||||
{{ item }}
|
||||
</span>
|
||||
</gl-new-dropdown-item>
|
||||
<gl-new-dropdown-divider />
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider />
|
||||
</template>
|
||||
|
||||
<gl-new-dropdown-item v-for="(item, idx) in extraLinks" :key="idx" :href="item.url">
|
||||
<gl-dropdown-item v-for="(item, idx) in extraLinks" :key="idx" :href="item.url">
|
||||
<span class="pl-4">{{ item.text }}</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlTooltipDirective,
|
||||
GlIcon,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { mapState, mapGetters, mapActions } from 'vuex';
|
|||
import {
|
||||
GlDeprecatedButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlIcon,
|
||||
GlModalDirective,
|
||||
|
|
@ -24,8 +24,8 @@ export default {
|
|||
components: {
|
||||
GlDeprecatedButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlIcon,
|
||||
DuplicateDashboardModal,
|
||||
|
|
@ -157,13 +157,13 @@ export default {
|
|||
</template>
|
||||
|
||||
<template v-if="addingMetricsAvailable">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-gl-modal="$options.modalIds.addMetric"
|
||||
data-qa-selector="add_metric_button"
|
||||
data-testid="add-metric-item"
|
||||
>
|
||||
{{ $options.i18n.addMetric }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
<gl-modal
|
||||
ref="addMetricModal"
|
||||
:modal-id="$options.modalIds.addMetric"
|
||||
|
|
@ -194,20 +194,20 @@ export default {
|
|||
</gl-modal>
|
||||
</template>
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-if="isMenuItemEnabled.addPanel"
|
||||
data-testid="add-panel-item-enabled"
|
||||
:to="newPanelPageLocation"
|
||||
>
|
||||
{{ $options.i18n.addPanel }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<!--
|
||||
wrapper for tooltip as button can be `disabled`
|
||||
https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
|
||||
-->
|
||||
<div v-else v-gl-tooltip :title="$options.i18n.addPanelInfo">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
:alt="$options.i18n.addPanelInfo"
|
||||
:to="newPanelPageLocation"
|
||||
data-testid="add-panel-item-disabled"
|
||||
|
|
@ -215,24 +215,24 @@ export default {
|
|||
class="gl-cursor-not-allowed"
|
||||
>
|
||||
<span class="gl-text-gray-400">{{ $options.i18n.addPanel }}</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</div>
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-if="isMenuItemEnabled.editDashboard"
|
||||
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
|
||||
data-qa-selector="edit_dashboard_button_enabled"
|
||||
data-testid="edit-dashboard-item-enabled"
|
||||
>
|
||||
{{ $options.i18n.editDashboard }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<!--
|
||||
wrapper for tooltip as button can be `disabled`
|
||||
https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
|
||||
-->
|
||||
<div v-else v-gl-tooltip :title="$options.i18n.editDashboardInfo">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
:alt="$options.i18n.editDashboardInfo"
|
||||
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
|
||||
data-testid="edit-dashboard-item-disabled"
|
||||
|
|
@ -240,16 +240,16 @@ export default {
|
|||
class="gl-cursor-not-allowed"
|
||||
>
|
||||
<span class="gl-text-gray-400">{{ $options.i18n.editDashboard }}</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</div>
|
||||
|
||||
<template v-if="isMenuItemShown.duplicateDashboard">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-gl-modal="$options.modalIds.duplicateDashboard"
|
||||
data-testid="duplicate-dashboard-item"
|
||||
>
|
||||
{{ $options.i18n.duplicateDashboard }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<duplicate-dashboard-modal
|
||||
:default-branch="defaultBranch"
|
||||
|
|
@ -259,25 +259,25 @@ export default {
|
|||
/>
|
||||
</template>
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-if="selectedDashboard"
|
||||
data-testid="star-dashboard-item"
|
||||
:disabled="isUpdatingStarredValue"
|
||||
@click="toggleStarredValue()"
|
||||
>
|
||||
{{ selectedDashboard.starred ? $options.i18n.unstarDashboard : $options.i18n.starDashboard }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-gl-modal="$options.modalIds.createDashboard"
|
||||
data-testid="create-dashboard-item"
|
||||
:disabled="!isMenuItemEnabled.createDashboard"
|
||||
:class="{ 'monitoring-actions-item-disabled': !isMenuItemEnabled.createDashboard }"
|
||||
>
|
||||
{{ $options.i18n.createDashboard }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
|
||||
<template v-if="isMenuItemEnabled.createDashboard">
|
||||
<create-dashboard-modal
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
GlButton,
|
||||
GlDropdown,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
GlSearchBoxByType,
|
||||
GlModalDirective,
|
||||
|
|
@ -30,7 +30,7 @@ export default {
|
|||
GlButton,
|
||||
GlDropdown,
|
||||
GlLoadingIcon,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
|
||||
GlSearchBoxByType,
|
||||
|
|
@ -196,7 +196,7 @@ export default {
|
|||
|
||||
<gl-loading-icon v-if="environmentsLoading" :inline="true" />
|
||||
<div v-else class="flex-fill overflow-auto">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="environment in filteredEnvironments"
|
||||
:key="environment.id"
|
||||
:is-check-item="true"
|
||||
|
|
@ -204,7 +204,7 @@ export default {
|
|||
:href="getEnvironmentPath(environment.id)"
|
||||
>
|
||||
{{ environment.name }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</div>
|
||||
<div
|
||||
v-show="shouldShowEnvironmentsDropdownNoMatchedMsg"
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import {
|
|||
GlLink,
|
||||
GlLoadingIcon,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlSprintf,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { mapState, mapGetters } from 'vuex';
|
|||
import {
|
||||
GlIcon,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlSearchBoxByType,
|
||||
GlModalDirective,
|
||||
} from '@gitlab/ui';
|
||||
|
|
@ -18,9 +18,9 @@ export default {
|
|||
components: {
|
||||
GlIcon,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlSearchBoxByType,
|
||||
},
|
||||
directives: {
|
||||
|
|
@ -87,7 +87,7 @@ export default {
|
|||
/>
|
||||
|
||||
<div class="flex-fill overflow-auto">
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="dashboard in starredDashboards"
|
||||
:key="dashboard.path"
|
||||
:is-check-item="true"
|
||||
|
|
@ -100,13 +100,13 @@ export default {
|
|||
</span>
|
||||
<gl-icon class="text-muted gl-flex-shrink-0 gl-ml-3 gl-align-self-center" name="star" />
|
||||
</div>
|
||||
</gl-new-dropdown-item>
|
||||
<gl-new-dropdown-divider
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider
|
||||
v-if="starredDashboards.length && nonStarredDashboards.length"
|
||||
ref="starredListDivider"
|
||||
/>
|
||||
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="dashboard in nonStarredDashboards"
|
||||
:key="dashboard.path"
|
||||
:is-check-item="true"
|
||||
|
|
@ -116,7 +116,7 @@ export default {
|
|||
<span class="gl-overflow-hidden gl-overflow-wrap-break">
|
||||
{{ dashboardDisplayName(dashboard) }}
|
||||
</span>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import {
|
|||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { n__, __ } from '~/locale';
|
||||
|
|
@ -49,8 +49,8 @@ export default {
|
|||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -158,20 +158,20 @@ export default {
|
|||
:title="s__('Metrics|Set refresh rate')"
|
||||
:text="dropdownText"
|
||||
>
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
:is-check-item="true"
|
||||
:is-checked="refreshInterval === null"
|
||||
@click="removeRefreshInterval()"
|
||||
>{{ __('Off') }}</gl-new-dropdown-item
|
||||
>{{ __('Off') }}</gl-dropdown-item
|
||||
>
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item
|
||||
v-for="(option, i) in $options.refreshIntervals"
|
||||
:key="i"
|
||||
:is-check-item="true"
|
||||
:is-checked="isChecked(option)"
|
||||
@click="setRefreshInterval(option)"
|
||||
>{{ option.label }}</gl-new-dropdown-item
|
||||
>{{ option.label }}</gl-dropdown-item
|
||||
>
|
||||
</gl-dropdown>
|
||||
</gl-button-group>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
GlFormSelect,
|
||||
GlLink,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
|
|
@ -38,7 +38,7 @@ export default {
|
|||
GlFormSelect,
|
||||
GlLink,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
},
|
||||
|
|
@ -179,7 +179,7 @@ export default {
|
|||
:placeholder="__('Search branches and tags')"
|
||||
class="gl-p-2"
|
||||
/>
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="(ref, index) in filteredRefs"
|
||||
:key="index"
|
||||
class="gl-font-monospace"
|
||||
|
|
@ -188,7 +188,7 @@ export default {
|
|||
@click="setRefSelected(ref)"
|
||||
>
|
||||
{{ ref }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
|
||||
<template #description>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { mapState, mapActions } from 'vuex';
|
|||
import {
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { redirectTo } from '~/lib/utils/url_utility';
|
||||
|
|
@ -20,9 +20,9 @@ export default {
|
|||
components: {
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlSearchBoxByType,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -116,18 +116,18 @@ export default {
|
|||
<gl-new-dropdown-header>
|
||||
{{ __('Search by author') }}
|
||||
</gl-new-dropdown-header>
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
<gl-search-box-by-type
|
||||
v-model.trim="authorInput"
|
||||
class="gl-m-3"
|
||||
:placeholder="__('Search')"
|
||||
@input="searchAuthors"
|
||||
/>
|
||||
<gl-new-dropdown-item :is-checked="!currentAuthor" @click="selectAuthor(null)">
|
||||
<gl-dropdown-item :is-checked="!currentAuthor" @click="selectAuthor(null)">
|
||||
{{ __('Any Author') }}
|
||||
</gl-new-dropdown-item>
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-new-dropdown-item
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-item
|
||||
v-for="author in commitsAuthors"
|
||||
:key="author.id"
|
||||
:is-checked="author.name === currentAuthor"
|
||||
|
|
@ -136,7 +136,7 @@ export default {
|
|||
@click="selectAuthor(author)"
|
||||
>
|
||||
{{ author.name }}
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import { GlNewDropdownHeader, GlNewDropdownItem, GlBadge, GlIcon } from '@gitlab/ui';
|
||||
import { GlNewDropdownHeader, GlDropdownItem, GlBadge, GlIcon } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
name: 'RefResultsSection',
|
||||
components: {
|
||||
GlNewDropdownHeader,
|
||||
GlNewDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlBadge,
|
||||
GlIcon,
|
||||
},
|
||||
|
|
@ -97,7 +97,7 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<gl-new-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="item in items"
|
||||
:key="item.name"
|
||||
@click="$emit('selected', item.value || item.name)"
|
||||
|
|
@ -118,7 +118,7 @@ export default {
|
|||
s__('DefaultBranchLabel|default')
|
||||
}}</gl-badge>
|
||||
</div>
|
||||
</gl-new-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import {
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
|
|
@ -19,7 +19,7 @@ export default {
|
|||
store: createStore(),
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlNewDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlNewDropdownHeader,
|
||||
GlSearchBoxByType,
|
||||
GlSprintf,
|
||||
|
|
@ -134,7 +134,7 @@ export default {
|
|||
<span class="gl-text-center gl-display-block">{{ i18n.dropdownHeader }}</span>
|
||||
</gl-new-dropdown-header>
|
||||
|
||||
<gl-new-dropdown-divider />
|
||||
<gl-dropdown-divider />
|
||||
|
||||
<gl-search-box-by-type
|
||||
ref="searchBox"
|
||||
|
|
@ -175,7 +175,7 @@ export default {
|
|||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-new-dropdown-divider v-if="showTagsSection || showCommitsSection" />
|
||||
<gl-dropdown-divider v-if="showTagsSection || showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showTagsSection">
|
||||
|
|
@ -190,7 +190,7 @@ export default {
|
|||
@selected="selectRef($event)"
|
||||
/>
|
||||
|
||||
<gl-new-dropdown-divider v-if="showCommitsSection" />
|
||||
<gl-dropdown-divider v-if="showCommitsSection" />
|
||||
</template>
|
||||
|
||||
<template v-if="showCommitsSection">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownHeader as GlDropdownHeader,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { n__, s__, sprintf } from '~/locale';
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
<script>
|
||||
import {
|
||||
GlIcon,
|
||||
GlButton,
|
||||
GlDeprecatedDropdown,
|
||||
GlDeprecatedDropdownItem,
|
||||
GlFormGroup,
|
||||
} from '@gitlab/ui';
|
||||
import { GlIcon, GlButton, GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui';
|
||||
import { __, sprintf } from '~/locale';
|
||||
|
||||
import { convertToFixedRange, isEqualTimeRanges, findTimeRange } from '~/lib/utils/datetime_range';
|
||||
|
|
@ -29,8 +23,8 @@ export default {
|
|||
components: {
|
||||
GlIcon,
|
||||
GlButton,
|
||||
GlDeprecatedDropdown,
|
||||
GlDeprecatedDropdownItem,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlFormGroup,
|
||||
TooltipOnTruncate,
|
||||
DateTimePickerInput,
|
||||
|
|
@ -212,7 +206,7 @@ export default {
|
|||
placement="top"
|
||||
class="d-inline-block"
|
||||
>
|
||||
<gl-deprecated-dropdown
|
||||
<gl-dropdown
|
||||
ref="dropdown"
|
||||
:text="timeWindowText"
|
||||
v-bind="$attrs"
|
||||
|
|
@ -269,7 +263,7 @@ export default {
|
|||
<span class="gl-pl-7">{{ __('Quick range') }}</span>
|
||||
</template>
|
||||
|
||||
<gl-deprecated-dropdown-item
|
||||
<gl-dropdown-item
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
data-qa-selector="quick_range_item"
|
||||
|
|
@ -283,9 +277,9 @@ export default {
|
|||
:class="{ invisible: !isOptionActive(option) }"
|
||||
/>
|
||||
{{ option.label }}
|
||||
</gl-deprecated-dropdown-item>
|
||||
</gl-dropdown-item>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
</gl-deprecated-dropdown>
|
||||
</gl-dropdown>
|
||||
</tooltip-on-truncate>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export default {
|
|||
this.focusedIndex = 0;
|
||||
}
|
||||
|
||||
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
|
||||
Mousetrap.bind(['t', 'mod+p'], e => {
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ export default {
|
|||
el.classList.contains('inputarea')
|
||||
) {
|
||||
return true;
|
||||
} else if (combo === 'command+p' || combo === 'ctrl+p') {
|
||||
} else if (combo === 'mod+p') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
GlDropdownItem,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlToken,
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { debounce } from 'lodash';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { debounce } from 'lodash';
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ module Types
|
|||
description: 'Timestamp of when the release was created'
|
||||
field :released_at, Types::TimeType, null: true,
|
||||
description: 'Timestamp of when the release was released'
|
||||
field :upcoming_release, GraphQL::BOOLEAN_TYPE, null: true, method: :upcoming_release?,
|
||||
description: 'Indicates the release is an upcoming release'
|
||||
field :assets, Types::ReleaseAssetsType, null: true, method: :itself,
|
||||
description: 'Assets of the release'
|
||||
field :links, Types::ReleaseLinksType, null: true, method: :itself,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ module AlertManagement
|
|||
project,
|
||||
user,
|
||||
title: alert_presenter.title,
|
||||
description: alert_presenter.issue_description
|
||||
description: alert_presenter.issue_description,
|
||||
severity: alert.severity
|
||||
).execute
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ module IncidentManagement
|
|||
class CreateService < BaseService
|
||||
ISSUE_TYPE = 'incident'
|
||||
|
||||
def initialize(project, current_user, title:, description:)
|
||||
def initialize(project, current_user, title:, description:, severity: IssuableSeverity::DEFAULT)
|
||||
super(project, current_user)
|
||||
|
||||
@title = title
|
||||
@description = description
|
||||
@severity = severity
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -23,12 +24,14 @@ module IncidentManagement
|
|||
|
||||
return error(issue.errors.full_messages.to_sentence, issue) unless issue.valid?
|
||||
|
||||
issue.update_severity(severity)
|
||||
|
||||
success(issue)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :title, :description
|
||||
attr_reader :title, :description, :severity
|
||||
|
||||
def success(issue)
|
||||
ServiceResponse.success(payload: { issue: issue })
|
||||
|
|
|
|||
|
|
@ -34,10 +34,12 @@
|
|||
%p.validation-pending.gl-field-error-ignore.field-validation.hide= _('Checking group URL availability...')
|
||||
|
||||
- if @group.persisted?
|
||||
.alert.alert-warning.gl-mt-3
|
||||
= _('Changing group URL can have unintended side effects.')
|
||||
= succeed '.' do
|
||||
= link_to _('Learn more'), help_page_path('user/group/index', anchor: 'changing-a-groups-path'), target: '_blank'
|
||||
.gl-alert.gl-alert-warning.gl-mt-3.gl-mb-3
|
||||
= sprite_icon('warning', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
|
||||
.gl-alert-body
|
||||
= _('Changing group URL can have unintended side effects.')
|
||||
= succeed '.' do
|
||||
= link_to _('Learn more'), help_page_path('user/group/index', anchor: 'changing-a-groups-path'), target: '_blank', class: 'gl-link'
|
||||
|
||||
- if @group.persisted?
|
||||
.row
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add user mapping by username when importing projects for Bitbucket Server importer
|
||||
merge_request: 36885
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add the Query Apdex Prometheus metric to usage ping
|
||||
merge_request: 39256
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace bootstrap alerts in app/views/shared/_group_form.html.haml
|
||||
merge_request: 41348
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Set incident severity when it is created from an alert.
|
||||
merge_request: 42072
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add support for environment_url.txt to API Fuzzing
|
||||
merge_request: 41523
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use 'read' method to get request body in Conan to fix uploads when using Unicorn
|
||||
merge_request: 41801
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fixes Auto DevOps deploy script for multiple additional hosts separated by
|
||||
comma and space
|
||||
merge_request: 41404
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add "upcomingRelease" field to GraphQL endpoint
|
||||
merge_request: 41183
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Stop applying Ctrl+P shortcut on MR page on Mac
|
||||
merge_request: 42240
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: bitbucket_server_user_mapping_by_username
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36885
|
||||
rollout_issue_url:
|
||||
group: group::import
|
||||
type: development
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: kubernetes_agent_internal_api
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41045
|
||||
rollout_issue_url:
|
||||
group: group::configure
|
||||
type: ops
|
||||
default_enabled: true
|
||||
|
|
@ -14358,6 +14358,11 @@ type Release {
|
|||
Relative web path to the tag associated with the release
|
||||
"""
|
||||
tagPath: String
|
||||
|
||||
"""
|
||||
Indicates the release is an upcoming release
|
||||
"""
|
||||
upcomingRelease: Boolean
|
||||
}
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -41832,6 +41832,20 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "upcomingRelease",
|
||||
"description": "Indicates the release is an upcoming release",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
|
|
|||
|
|
@ -1959,6 +1959,7 @@ Represents a release
|
|||
| `releasedAt` | Time | Timestamp of when the release was released |
|
||||
| `tagName` | String | Name of the tag associated with the release |
|
||||
| `tagPath` | String | Relative web path to the tag associated with the release |
|
||||
| `upcomingRelease` | Boolean | Indicates the release is an upcoming release |
|
||||
|
||||
### ReleaseAssetLink
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ This tutorial demonstrates how to authenticate, configure, and read secrets with
|
|||
NOTE: **Note:**
|
||||
[GitLab Premium](https://about.gitlab.com/pricing/) supports read access to a
|
||||
Hashicorp Vault, and enables you to
|
||||
[use Vault secrets in a CI job](../../secrets/index.md#use-vault-secrets-in-a-ci-job-premium).
|
||||
[use Vault secrets in a CI job](../../secrets/index.md#use-vault-secrets-in-a-ci-job).
|
||||
To learn more, read [Using external secrets in CI](../../secrets/index.md).
|
||||
|
||||
## Requirements
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ the [JSON Web Token](https://gitlab.com/gitlab-org/gitlab/-/issues/207125) (`CI_
|
|||
introduced in GitLab 12.10.
|
||||
|
||||
You must [configure your Vault server](#configure-your-vault-server) before you
|
||||
can use [use Vault secrets in a CI job](#use-vault-secrets-in-a-ci-job-premium).
|
||||
can use [use Vault secrets in a CI job](#use-vault-secrets-in-a-ci-job).
|
||||
|
||||
NOTE: **Note:**
|
||||
Read the [Authenticating and Reading Secrets With Hashicorp Vault](../examples/authenticating-with-hashicorp-vault/index.md)
|
||||
|
|
@ -104,7 +104,7 @@ The path to this file is stored in environment variable named `DATABASE_PASSWORD
|
|||
similar to [CI variables of type `file`](../variables/README.md#custom-environment-variables-of-type-file).
|
||||
|
||||
For more information about the supported syntax, read the
|
||||
[`.gitlab-ci.yml` reference](../yaml/README.md#secretsvault-premium).
|
||||
[`.gitlab-ci.yml` reference](../yaml/README.md#secretsvault).
|
||||
|
||||
## Configure Vault server roles
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,23 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Kubernetes agent information
|
||||
## Kubernetes agent endpoints
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41045) in GitLab 13.4.
|
||||
> - This feature is not deployed on GitLab.com
|
||||
> - It's not recommended for production use.
|
||||
|
||||
The following endpoints are used by the GitLab Kubernetes Agent Server (kas)
|
||||
for various purposes.
|
||||
|
||||
These endpoints are all authenticated using JWT. The JWT secret is stored in a file
|
||||
specified in `config/gitlab.yml`. By default, the location is in the root of the
|
||||
GitLab Rails app in a file called `.gitlab_kas_secret`.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
The Kubernetes agent is under development and is not recommended for production use.
|
||||
|
||||
### Kubernetes agent information
|
||||
|
||||
Called from GitLab Kubernetes Agent Server (kas) to retrieve agent
|
||||
information for the given agent token. This returns the Gitaly connection
|
||||
|
|
@ -388,7 +404,7 @@ Example Request:
|
|||
curl --request GET --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" "http://localhost:3000/api/v4/internal/kubernetes/agent_info"
|
||||
```
|
||||
|
||||
## Kubernetes agent project information
|
||||
### Kubernetes agent project information
|
||||
|
||||
Called from GitLab Kubernetes Agent Server (kas) to retrieve project
|
||||
information for the given agent token. This returns the Gitaly
|
||||
|
|
@ -413,7 +429,7 @@ Example Request:
|
|||
curl --request GET --header "Gitlab-Kas-Api-Request: <JWT token>" --header "Authorization: Bearer <agent token>" "http://localhost:3000/api/v4/internal/kubernetes/project_info?id=7"
|
||||
```
|
||||
|
||||
## Kubernetes agent usage metrics
|
||||
### Kubernetes agent usage metrics
|
||||
|
||||
Called from GitLab Kubernetes Agent Server (kas) to increase the usage
|
||||
metric counters.
|
||||
|
|
|
|||
|
|
@ -762,6 +762,7 @@ The following is example content of the Usage Ping payload.
|
|||
"topology": {
|
||||
"duration_s": 0.013836685999194742,
|
||||
"application_requests_per_hour": 4224,
|
||||
"query_apdex_weekly_average": 0.996,
|
||||
"failures": [],
|
||||
"nodes": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -107,7 +107,15 @@ Follow these steps to configure API fuzzing in GitLab with an OpenAPI specificat
|
|||
```
|
||||
|
||||
1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
|
||||
variable:
|
||||
variable or an `environment_url.txt` file.
|
||||
|
||||
Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
|
||||
dynamic environments. To run API fuzzing against an app dynamically created during a GitLab CI/CD
|
||||
pipeline, have the app persist its domain in an `environment_url.txt` file. API fuzzing
|
||||
automatically parses that file to find its scan target. You can see an
|
||||
[example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
|
||||
Here's an example of using `FUZZAPI_TARGET_URL`:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
|
|
@ -189,7 +197,16 @@ target API to test:
|
|||
FUZZAPI_HAR: test-api-specification.json
|
||||
```
|
||||
|
||||
1. Add the `FUZZAPI_TARGET_URL` variable and set it to the target API instance's base URL:
|
||||
1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
|
||||
variable or an `environment_url.txt` file.
|
||||
|
||||
Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
|
||||
dynamic environments. To run API fuzzing against an app dynamically created during a GitLab CI/CD
|
||||
pipeline, have the app persist its domain in an `environment_url.txt` file. API fuzzing
|
||||
automatically parses that file to find its scan target. You can see an
|
||||
[example of this in our Auto DevOps CI YAML](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml).
|
||||
|
||||
Here's an example of using `FUZZAPI_TARGET_URL`:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
|
|
|
|||
|
|
@ -89,3 +89,10 @@ is created, you are ready to [upload your package](../conan_repository/index.md#
|
|||
```shell
|
||||
CONAN_LOGIN_USERNAME=<gitlab-username> CONAN_PASSWORD=<personal_access_token> conan upload MyPackage/1.0.0@foo+bar+my-proj/channel --all --remote=gitlab
|
||||
```
|
||||
|
||||
#### Composer
|
||||
|
||||
It is currently not possible to publish a Composer package to a project that is different from where its code resides.
|
||||
|
||||
If you attempt to publish a Composer package to a different project, you get a `404 Branch Not Found`
|
||||
or `404 Tag Not Found` error.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,25 @@ The importer will create any new namespaces (groups) if they don't exist or in
|
|||
the case the namespace is taken, the repository will be imported under the user's
|
||||
namespace that started the import process.
|
||||
|
||||
#### User assignment by username
|
||||
|
||||
Alternatively, user assignment by username is available behind a `bitbucket_server_user_mapping_by_username` feature flag.
|
||||
The importer will try to find a user in the GitLab user database using author's `username` or `slug` or `displayName`.
|
||||
Falls back to author's `email` if user is not found by username.
|
||||
Similarly to user assignment by email, if no such user is available, the project creator is set as the author.
|
||||
|
||||
To enable or disable user assignment by username:
|
||||
|
||||
Start a [Rails console](../../../administration/troubleshooting/debug.md#starting-a-rails-console-session).
|
||||
|
||||
```ruby
|
||||
# Enable
|
||||
Feature.enable(:bitbucket_server_user_mapping_by_username)
|
||||
|
||||
# Disable
|
||||
Feature.disable(:bitbucket_server_user_mapping_by_username)
|
||||
```
|
||||
|
||||
## Importing your Bitbucket repositories
|
||||
|
||||
1. Sign in to GitLab and go to your dashboard.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ module API
|
|||
end
|
||||
|
||||
def check_feature_enabled
|
||||
not_found! unless Feature.enabled?(:kubernetes_agent_internal_api, default_enabled: true)
|
||||
not_found! unless Feature.enabled?(:kubernetes_agent_internal_api, default_enabled: true, type: :ops)
|
||||
end
|
||||
|
||||
def check_agent_token
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ module BitbucketServer
|
|||
end
|
||||
|
||||
def author_username
|
||||
author['displayName']
|
||||
author['username'] ||
|
||||
author['slug'] ||
|
||||
author['displayName']
|
||||
end
|
||||
|
||||
def author_email
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ module BitbucketServer
|
|||
raw.dig('author', 'user', 'emailAddress')
|
||||
end
|
||||
|
||||
def author_username
|
||||
raw.dig('author', 'user', 'username') ||
|
||||
raw.dig('author', 'user', 'slug') ||
|
||||
raw.dig('author', 'user', 'displayName')
|
||||
end
|
||||
|
||||
def description
|
||||
raw['description']
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ class Feature
|
|||
superclass.table_name = 'feature_gates'
|
||||
end
|
||||
|
||||
class ActiveSupportCacheStoreAdapter < Flipper::Adapters::ActiveSupportCacheStore
|
||||
# overrides methods in EE
|
||||
end
|
||||
|
||||
InvalidFeatureFlagError = Class.new(Exception) # rubocop:disable Lint/InheritException
|
||||
|
||||
class << self
|
||||
|
|
@ -160,7 +164,7 @@ class Feature
|
|||
|
||||
# Redis L2 cache
|
||||
redis_cache_adapter =
|
||||
Flipper::Adapters::ActiveSupportCacheStore.new(
|
||||
ActiveSupportCacheStoreAdapter.new(
|
||||
active_record_adapter,
|
||||
l2_cache_backend,
|
||||
expires_in: 1.hour)
|
||||
|
|
@ -237,4 +241,4 @@ class Feature
|
|||
end
|
||||
end
|
||||
|
||||
Feature.prepend_if_ee('EE::Feature')
|
||||
Feature::ActiveSupportCacheStoreAdapter.prepend_if_ee('EE::Feature::ActiveSupportCacheStoreAdapter')
|
||||
|
|
|
|||
|
|
@ -61,17 +61,18 @@ module Gitlab
|
|||
}.to_json)
|
||||
end
|
||||
|
||||
def gitlab_user_id(email)
|
||||
find_user_id(email) || project.creator_id
|
||||
end
|
||||
def find_user_id(by:, value:)
|
||||
return unless value
|
||||
|
||||
def find_user_id(email)
|
||||
return unless email
|
||||
return users[value] if users.key?(value)
|
||||
|
||||
return users[email] if users.key?(email)
|
||||
user = if by == :email
|
||||
User.find_by_any_email(value, confirmed: true)
|
||||
else
|
||||
User.find_by_username(value)
|
||||
end
|
||||
|
||||
user = User.find_by_any_email(email, confirmed: true)
|
||||
users[email] = user&.id
|
||||
users[value] = user&.id
|
||||
|
||||
user&.id
|
||||
end
|
||||
|
|
@ -197,9 +198,8 @@ module Gitlab
|
|||
log_info(stage: 'import_bitbucket_pull_requests', message: 'starting', iid: pull_request.iid)
|
||||
|
||||
description = ''
|
||||
description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email)
|
||||
description += author_line(pull_request)
|
||||
description += pull_request.description if pull_request.description
|
||||
author_id = gitlab_user_id(pull_request.author_email)
|
||||
|
||||
attributes = {
|
||||
iid: pull_request.iid,
|
||||
|
|
@ -212,7 +212,7 @@ module Gitlab
|
|||
target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name),
|
||||
target_branch_sha: pull_request.target_branch_sha,
|
||||
state_id: MergeRequest.available_states[pull_request.state],
|
||||
author_id: author_id,
|
||||
author_id: author_id(pull_request),
|
||||
created_at: pull_request.created_at,
|
||||
updated_at: pull_request.updated_at
|
||||
}
|
||||
|
|
@ -254,7 +254,7 @@ module Gitlab
|
|||
|
||||
committer = merge_event.committer_email
|
||||
|
||||
user_id = gitlab_user_id(committer)
|
||||
user_id = find_user_id(by: :email, value: committer) || project.creator_id
|
||||
timestamp = merge_event.merge_timestamp
|
||||
merge_request.update({ merge_commit_sha: merge_event.merge_commit })
|
||||
metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request)
|
||||
|
|
@ -353,7 +353,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def pull_request_comment_attributes(comment)
|
||||
author = find_user_id(comment.author_email)
|
||||
author = uid(comment)
|
||||
note = ''
|
||||
|
||||
unless author
|
||||
|
|
@ -397,6 +397,23 @@ module Gitlab
|
|||
def metrics
|
||||
@metrics ||= Gitlab::Import::Metrics.new(:bitbucket_server_importer, @project)
|
||||
end
|
||||
|
||||
def author_line(rep_object)
|
||||
return '' if uid(rep_object)
|
||||
|
||||
@formatter.author_line(rep_object.author)
|
||||
end
|
||||
|
||||
def author_id(rep_object)
|
||||
uid(rep_object) || project.creator_id
|
||||
end
|
||||
|
||||
def uid(rep_object)
|
||||
find_user_id(by: :email, value: rep_object.author_email) unless Feature.enabled?(:bitbucket_server_user_mapping_by_username)
|
||||
|
||||
find_user_id(by: :username, value: rep_object.author_username) ||
|
||||
find_user_id(by: :email, value: rep_object.author_email)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ module Gitlab
|
|||
with_prometheus_client(fallback: {}) do |client|
|
||||
{
|
||||
application_requests_per_hour: topology_app_requests_per_hour(client),
|
||||
query_apdex_weekly_average: topology_query_apdex_weekly_average(client),
|
||||
nodes: topology_node_data(client)
|
||||
}.compact
|
||||
end
|
||||
|
|
@ -63,6 +64,16 @@ module Gitlab
|
|||
(result['value'].last.to_f * 1.hour).to_i
|
||||
end
|
||||
|
||||
def topology_query_apdex_weekly_average(client)
|
||||
result = query_safely('gitlab_usage_ping:sql_duration_apdex:ratio_rate5m', 'query_apdex', fallback: nil) do |query|
|
||||
client.query(aggregate_one_week(query)).first
|
||||
end
|
||||
|
||||
return unless result
|
||||
|
||||
result['value'].last.to_f
|
||||
end
|
||||
|
||||
def topology_node_data(client)
|
||||
# node-level data
|
||||
by_instance_mem = topology_node_memory(client)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ module QA
|
|||
end
|
||||
|
||||
def select_project
|
||||
wait_until(sleep_interval: 2, reload: false) do
|
||||
has_element? :project_list_item
|
||||
end
|
||||
click_element :project_list_item
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [
|
||||
|
|
@ -38,7 +39,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [
|
||||
|
|
@ -56,7 +58,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -85,7 +88,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"createdDate": 1530164016725,
|
||||
|
|
@ -115,7 +119,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"createdDate": 1530164026000,
|
||||
|
|
@ -147,7 +152,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -194,7 +200,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -363,7 +370,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -383,7 +391,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -543,7 +552,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -563,7 +573,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [
|
||||
|
|
@ -581,7 +592,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [
|
||||
|
|
@ -599,7 +611,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -789,7 +802,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -809,7 +823,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -843,7 +858,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -863,7 +879,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"authorTimestamp": 1529727872000,
|
||||
|
|
@ -880,7 +897,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"committerTimestamp": 1529727872000,
|
||||
|
|
@ -951,7 +969,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -971,7 +990,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [
|
||||
|
|
@ -989,7 +1009,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -1038,7 +1059,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -1058,7 +1080,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
},
|
||||
"comments": [],
|
||||
|
|
@ -1092,7 +1115,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
@ -1113,7 +1137,8 @@
|
|||
]
|
||||
},
|
||||
"name": "root",
|
||||
"slug": "root",
|
||||
"slug": "slug",
|
||||
"username": "username",
|
||||
"type": "NORMAL"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@
|
|||
"status":"UNAPPROVED",
|
||||
"user":{
|
||||
"active":true,
|
||||
"displayName":"root",
|
||||
"displayName":"displayName",
|
||||
"emailAddress":"joe.montana@49ers.com",
|
||||
"username": "username",
|
||||
"id":1,
|
||||
"links":{
|
||||
"self":[
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
]
|
||||
},
|
||||
"name":"root",
|
||||
"slug":"root",
|
||||
"slug":"slug",
|
||||
"type":"NORMAL"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ exports[`Design management design version dropdown component renders design vers
|
|||
text="Showing latest version"
|
||||
variant="default"
|
||||
>
|
||||
<gl-new-dropdown-item-stub
|
||||
<gl-dropdown-item-stub
|
||||
avatarurl=""
|
||||
iconcolor=""
|
||||
iconname=""
|
||||
|
|
@ -22,8 +22,8 @@ exports[`Design management design version dropdown component renders design vers
|
|||
Version
|
||||
2
|
||||
(latest)
|
||||
</gl-new-dropdown-item-stub>
|
||||
<gl-new-dropdown-item-stub
|
||||
</gl-dropdown-item-stub>
|
||||
<gl-dropdown-item-stub
|
||||
avatarurl=""
|
||||
iconcolor=""
|
||||
iconname=""
|
||||
|
|
@ -34,7 +34,7 @@ exports[`Design management design version dropdown component renders design vers
|
|||
Version
|
||||
1
|
||||
|
||||
</gl-new-dropdown-item-stub>
|
||||
</gl-dropdown-item-stub>
|
||||
</gl-dropdown-stub>
|
||||
`;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ exports[`Design management design version dropdown component renders design vers
|
|||
text="Showing latest version"
|
||||
variant="default"
|
||||
>
|
||||
<gl-new-dropdown-item-stub
|
||||
<gl-dropdown-item-stub
|
||||
avatarurl=""
|
||||
iconcolor=""
|
||||
iconname=""
|
||||
|
|
@ -60,8 +60,8 @@ exports[`Design management design version dropdown component renders design vers
|
|||
Version
|
||||
2
|
||||
(latest)
|
||||
</gl-new-dropdown-item-stub>
|
||||
<gl-new-dropdown-item-stub
|
||||
</gl-dropdown-item-stub>
|
||||
<gl-dropdown-item-stub
|
||||
avatarurl=""
|
||||
iconcolor=""
|
||||
iconname=""
|
||||
|
|
@ -72,6 +72,6 @@ exports[`Design management design version dropdown component renders design vers
|
|||
Version
|
||||
1
|
||||
|
||||
</gl-new-dropdown-item-stub>
|
||||
</gl-dropdown-item-stub>
|
||||
</gl-dropdown-stub>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlDropdown, GlNewDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
|
||||
import DesignVersionDropdown from '~/design_management/components/upload/design_version_dropdown.vue';
|
||||
import mockAllVersions from './mock_data/all_versions';
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ describe('Design management design version dropdown component', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findVersionLink = index => wrapper.findAll(GlNewDropdownItem).at(index);
|
||||
const findVersionLink = index => wrapper.findAll(GlDropdownItem).at(index);
|
||||
|
||||
it('renders design version dropdown button', () => {
|
||||
createComponent();
|
||||
|
|
@ -107,7 +107,7 @@ describe('Design management design version dropdown component', () => {
|
|||
createComponent();
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.findAll(GlNewDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
|
||||
expect(wrapper.findAll(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ exports[`Alert integration settings form default state should match the default
|
|||
text="selecte_tmpl"
|
||||
variant="default"
|
||||
>
|
||||
<gl-new-dropdown-item-stub
|
||||
<gl-dropdown-item-stub
|
||||
avatarurl=""
|
||||
data-qa-selector="incident_templates_item"
|
||||
iconcolor=""
|
||||
|
|
@ -67,7 +67,7 @@ exports[`Alert integration settings form default state should match the default
|
|||
|
||||
No template selected
|
||||
|
||||
</gl-new-dropdown-item-stub>
|
||||
</gl-dropdown-item-stub>
|
||||
</gl-dropdown-stub>
|
||||
</gl-form-group-stub>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlNewDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import { DASHBOARD_PAGE, PANEL_NEW_PAGE } from '~/monitoring/router/constants';
|
||||
import { setupAllDashboards, setupStoreWithData } from '../store_utils';
|
||||
|
|
@ -146,8 +146,8 @@ describe('Actions menu', () => {
|
|||
});
|
||||
|
||||
describe('add panel item', () => {
|
||||
const GlNewDropdownItemStub = {
|
||||
extends: GlNewDropdownItem,
|
||||
const GlDropdownItemStub = {
|
||||
extends: GlDropdownItem,
|
||||
props: {
|
||||
to: [String, Object],
|
||||
},
|
||||
|
|
@ -164,7 +164,7 @@ describe('Actions menu', () => {
|
|||
},
|
||||
{
|
||||
mocks: { $route },
|
||||
stubs: { GlNewDropdownItem: GlNewDropdownItemStub },
|
||||
stubs: { GlDropdownItem: GlDropdownItemStub },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlNewDropdownItem, GlSearchBoxByType, GlLoadingIcon, GlButton } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlSearchBoxByType, GlLoadingIcon, GlButton } from '@gitlab/ui';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import * as types from '~/monitoring/stores/mutation_types';
|
||||
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
|
||||
|
|
@ -31,7 +31,7 @@ describe('Dashboard header', () => {
|
|||
const findDashboardDropdown = () => wrapper.find(DashboardsDropdown);
|
||||
|
||||
const findEnvsDropdown = () => wrapper.find({ ref: 'monitorEnvironmentsDropdown' });
|
||||
const findEnvsDropdownItems = () => findEnvsDropdown().findAll(GlNewDropdownItem);
|
||||
const findEnvsDropdownItems = () => findEnvsDropdown().findAll(GlDropdownItem);
|
||||
const findEnvsDropdownSearch = () => findEnvsDropdown().find(GlSearchBoxByType);
|
||||
const findEnvsDropdownSearchMsg = () => wrapper.find({ ref: 'monitorEnvironmentsDropdownMsg' });
|
||||
const findEnvsDropdownLoadingIcon = () => findEnvsDropdown().find(GlLoadingIcon);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Vuex from 'vuex';
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import { setTestTimeout } from 'helpers/timeout';
|
||||
import { GlNewDropdownItem as GlDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdownItem } from '@gitlab/ui';
|
||||
import invalidUrl from '~/lib/utils/invalid_url';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import AlertWidget from '~/monitoring/components/alert_widget.vue';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlNewDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
import { GlDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
|
||||
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
|
||||
|
||||
|
|
@ -33,8 +33,8 @@ describe('DashboardsDropdown', () => {
|
|||
});
|
||||
}
|
||||
|
||||
const findItems = () => wrapper.findAll(GlNewDropdownItem);
|
||||
const findItemAt = i => wrapper.findAll(GlNewDropdownItem).at(i);
|
||||
const findItems = () => wrapper.findAll(GlDropdownItem);
|
||||
const findItemAt = i => wrapper.findAll(GlDropdownItem).at(i);
|
||||
const findSearchInput = () => wrapper.find({ ref: 'monitorDashboardsDropdownSearch' });
|
||||
const findNoItemsMsg = () => wrapper.find({ ref: 'monitorDashboardsDropdownMsg' });
|
||||
const findStarredListDivider = () => wrapper.find({ ref: 'starredListDivider' });
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import Visibility from 'visibilityjs';
|
||||
import { GlDropdown, GlNewDropdownItem, GlButton } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import RefreshButton from '~/monitoring/components/refresh_button.vue';
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ describe('RefreshButton', () => {
|
|||
|
||||
const findRefreshBtn = () => wrapper.find(GlButton);
|
||||
const findDropdown = () => wrapper.find(GlDropdown);
|
||||
const findOptions = () => findDropdown().findAll(GlNewDropdownItem);
|
||||
const findOptions = () => findDropdown().findAll(GlDropdownItem);
|
||||
const findOptionAt = index => findOptions().at(index);
|
||||
|
||||
const expectFetchDataToHaveBeenCalledTimes = times => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { GlDropdown, GlNewDropdownItem, GlForm } from '@gitlab/ui';
|
||||
import { GlDropdown, GlDropdownItem, GlForm } from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
|
@ -24,7 +24,7 @@ describe('Pipeline New Form', () => {
|
|||
|
||||
const findForm = () => wrapper.find(GlForm);
|
||||
const findDropdown = () => wrapper.find(GlDropdown);
|
||||
const findDropdownItems = () => wrapper.findAll(GlNewDropdownItem);
|
||||
const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
|
||||
const findVariableRows = () => wrapper.findAll('[data-testid="ci-variable-row"]');
|
||||
const findRemoveIcons = () => wrapper.findAll('[data-testid="remove-ci-variable-row"]');
|
||||
const findKeyInputs = () => wrapper.findAll('[data-testid="pipeline-form-ci-variable-key"]');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import { GlDropdown, GlNewDropdownHeader, GlSearchBoxByType, GlNewDropdownItem } from '@gitlab/ui';
|
||||
import { GlDropdown, GlNewDropdownHeader, GlSearchBoxByType, GlDropdownItem } from '@gitlab/ui';
|
||||
import * as urlUtility from '~/lib/utils/url_utility';
|
||||
import AuthorSelect from '~/projects/commits/components/author_select.vue';
|
||||
import { createStore } from '~/projects/commits/store';
|
||||
|
|
@ -61,7 +61,7 @@ describe('Author Select', () => {
|
|||
const findDropdown = () => wrapper.find(GlDropdown);
|
||||
const findDropdownHeader = () => wrapper.find(GlNewDropdownHeader);
|
||||
const findSearchBox = () => wrapper.find(GlSearchBoxByType);
|
||||
const findDropdownItems = () => wrapper.findAll(GlNewDropdownItem);
|
||||
const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
|
||||
|
||||
describe('user is searching via "filter by commit message"', () => {
|
||||
it('disables dropdown container', () => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Vuex from 'vuex';
|
|||
import { mount, createLocalVue } from '@vue/test-utils';
|
||||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { GlLoadingIcon, GlSearchBoxByType, GlNewDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlSearchBoxByType, GlDropdownItem, GlIcon } from '@gitlab/ui';
|
||||
import { trimText } from 'helpers/text_helper';
|
||||
import { sprintf } from '~/locale';
|
||||
import { ENTER_KEY } from '~/lib/utils/keys';
|
||||
|
|
@ -87,15 +87,15 @@ describe('Ref selector component', () => {
|
|||
const findSearchBox = () => wrapper.find(GlSearchBoxByType);
|
||||
|
||||
const findBranchesSection = () => wrapper.find('[data-testid="branches-section"]');
|
||||
const findBranchDropdownItems = () => findBranchesSection().findAll(GlNewDropdownItem);
|
||||
const findBranchDropdownItems = () => findBranchesSection().findAll(GlDropdownItem);
|
||||
const findFirstBranchDropdownItem = () => findBranchDropdownItems().at(0);
|
||||
|
||||
const findTagsSection = () => wrapper.find('[data-testid="tags-section"]');
|
||||
const findTagDropdownItems = () => findTagsSection().findAll(GlNewDropdownItem);
|
||||
const findTagDropdownItems = () => findTagsSection().findAll(GlDropdownItem);
|
||||
const findFirstTagDropdownItem = () => findTagDropdownItems().at(0);
|
||||
|
||||
const findCommitsSection = () => wrapper.find('[data-testid="commits-section"]');
|
||||
const findCommitDropdownItems = () => findCommitsSection().findAll(GlNewDropdownItem);
|
||||
const findCommitDropdownItems = () => findCommitsSection().findAll(GlDropdownItem);
|
||||
const findFirstCommitDropdownItem = () => findCommitDropdownItems().at(0);
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -319,8 +319,8 @@ describe('File finder item spec', () => {
|
|||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('calls toggle on `command+p` key press', done => {
|
||||
Mousetrap.trigger('command+p');
|
||||
it('calls toggle on `mod+p` key press', done => {
|
||||
Mousetrap.trigger('mod+p');
|
||||
|
||||
vm.$nextTick()
|
||||
.then(() => {
|
||||
|
|
@ -330,33 +330,12 @@ describe('File finder item spec', () => {
|
|||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('calls toggle on `ctrl+p` key press', done => {
|
||||
Mousetrap.trigger('ctrl+p');
|
||||
|
||||
vm.$nextTick()
|
||||
.then(() => {
|
||||
expect(vm.toggle).toHaveBeenCalled();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('always allows `command+p` to trigger toggle', () => {
|
||||
it('always allows `mod+p` to trigger toggle', () => {
|
||||
expect(
|
||||
Mousetrap.prototype.stopCallback(
|
||||
null,
|
||||
vm.$el.querySelector('.dropdown-input-field'),
|
||||
'command+p',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('always allows `ctrl+p` to trigger toggle', () => {
|
||||
expect(
|
||||
Mousetrap.prototype.stopCallback(
|
||||
null,
|
||||
vm.$el.querySelector('.dropdown-input-field'),
|
||||
'ctrl+p',
|
||||
'mod+p',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import {
|
||||
GlFilteredSearch,
|
||||
GlButtonGroup,
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlNewDropdownItem as GlDropdownItem,
|
||||
} from '@gitlab/ui';
|
||||
import { GlFilteredSearch, GlButtonGroup, GlButton, GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
|
||||
import FilteredSearchBarRoot from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
||||
import { uniqueTokens } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlFilteredSearchToken,
|
||||
GlFilteredSearchTokenSegment,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
} from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlFilteredSearchTokenSegment,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
} from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlFilteredSearchTokenSegment,
|
||||
GlNewDropdownDivider as GlDropdownDivider,
|
||||
GlDropdownDivider,
|
||||
} from '@gitlab/ui';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
|
|
|||
|
|
@ -13,7 +13,30 @@ RSpec.describe BitbucketServer::Representation::Comment do
|
|||
end
|
||||
|
||||
describe '#author_username' do
|
||||
it { expect(subject.author_username).to eq('root' ) }
|
||||
it 'returns username' do
|
||||
expect(subject.author_username).to eq('username')
|
||||
end
|
||||
|
||||
context 'when username is absent' do
|
||||
before do
|
||||
comment['comment']['author'].delete('username')
|
||||
end
|
||||
|
||||
it 'returns slug' do
|
||||
expect(subject.author_username).to eq('slug')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when slug and username are absent' do
|
||||
before do
|
||||
comment['comment']['author'].delete('username')
|
||||
comment['comment']['author'].delete('slug')
|
||||
end
|
||||
|
||||
it 'returns displayName' do
|
||||
expect(subject.author_username).to eq('root')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#author_email' do
|
||||
|
|
|
|||
|
|
@ -15,6 +15,33 @@ RSpec.describe BitbucketServer::Representation::PullRequest do
|
|||
it { expect(subject.author_email).to eq('joe.montana@49ers.com') }
|
||||
end
|
||||
|
||||
describe '#author_username' do
|
||||
it 'returns username' do
|
||||
expect(subject.author_username).to eq('username')
|
||||
end
|
||||
|
||||
context 'when username is absent' do
|
||||
before do
|
||||
sample_data['author']['user'].delete('username')
|
||||
end
|
||||
|
||||
it 'returns slug' do
|
||||
expect(subject.author_username).to eq('slug')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when slug and username are absent' do
|
||||
before do
|
||||
sample_data['author']['user'].delete('username')
|
||||
sample_data['author']['user'].delete('slug')
|
||||
end
|
||||
|
||||
it 'returns displayName' do
|
||||
expect(subject.author_username).to eq('displayName')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#description' do
|
||||
it { expect(subject.description).to eq('Test') }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
include ImportSpecHelper
|
||||
|
||||
let(:import_url) { 'http://my-bitbucket' }
|
||||
let(:user) { 'bitbucket' }
|
||||
let(:bitbucket_user) { 'bitbucket' }
|
||||
let(:project_creator) { create(:user, username: 'project_creator', email: 'project_creator@example.org') }
|
||||
let(:password) { 'test' }
|
||||
let(:project) { create(:project, :repository, import_url: import_url) }
|
||||
let(:project) { create(:project, :repository, import_url: import_url, creator: project_creator) }
|
||||
let(:now) { Time.now.utc.change(usec: 0) }
|
||||
let(:project_key) { 'TEST' }
|
||||
let(:repo_slug) { 'rouge' }
|
||||
|
|
@ -19,7 +20,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
before do
|
||||
data = project.create_or_update_import_data(
|
||||
data: { project_key: project_key, repo_slug: repo_slug },
|
||||
credentials: { base_uri: import_url, user: user, password: password }
|
||||
credentials: { base_uri: import_url, user: bitbucket_user, password: password }
|
||||
)
|
||||
data.save
|
||||
project.save
|
||||
|
|
@ -51,12 +52,11 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
end
|
||||
|
||||
describe '#import_pull_requests' do
|
||||
before do
|
||||
allow(subject).to receive(:import_repository)
|
||||
allow(subject).to receive(:delete_temp_branches)
|
||||
allow(subject).to receive(:restore_branches)
|
||||
let(:pull_request_author) { create(:user, username: 'pull_request_author', email: 'pull_request_author@example.org') }
|
||||
let(:note_author) { create(:user, username: 'note_author', email: 'note_author@example.org') }
|
||||
|
||||
pull_request = instance_double(
|
||||
let(:pull_request) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::PullRequest,
|
||||
iid: 10,
|
||||
source_branch_sha: sample.commits.last,
|
||||
|
|
@ -67,65 +67,172 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
description: 'This is a test pull request',
|
||||
state: 'merged',
|
||||
author: 'Test Author',
|
||||
author_email: project.owner.email,
|
||||
author_email: pull_request_author.email,
|
||||
author_username: pull_request_author.username,
|
||||
created_at: Time.now,
|
||||
updated_at: Time.now,
|
||||
raw: {},
|
||||
merged?: true)
|
||||
end
|
||||
|
||||
allow(subject.client).to receive(:pull_requests).and_return([pull_request])
|
||||
|
||||
@merge_event = instance_double(
|
||||
let(:merge_event) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::Activity,
|
||||
comment?: false,
|
||||
merge_event?: true,
|
||||
committer_email: project.owner.email,
|
||||
committer_email: pull_request_author.email,
|
||||
merge_timestamp: now,
|
||||
merge_commit: '12345678'
|
||||
)
|
||||
end
|
||||
|
||||
@pr_note = instance_double(
|
||||
let(:pr_note) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::Comment,
|
||||
note: 'Hello world',
|
||||
author_email: 'unknown@gmail.com',
|
||||
author_username: 'The Flash',
|
||||
author_email: note_author.email,
|
||||
author_username: note_author.username,
|
||||
comments: [],
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
parent_comment: nil)
|
||||
end
|
||||
|
||||
@pr_comment = instance_double(
|
||||
let(:pr_comment) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::Activity,
|
||||
comment?: true,
|
||||
inline_comment?: false,
|
||||
merge_event?: false,
|
||||
comment: @pr_note)
|
||||
comment: pr_note)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:import_repository)
|
||||
allow(subject).to receive(:delete_temp_branches)
|
||||
allow(subject).to receive(:restore_branches)
|
||||
|
||||
allow(subject.client).to receive(:pull_requests).and_return([pull_request])
|
||||
end
|
||||
|
||||
it 'imports merge event' do
|
||||
expect(subject.client).to receive(:activities).and_return([@merge_event])
|
||||
expect(subject.client).to receive(:activities).and_return([merge_event])
|
||||
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.metrics.merged_by).to eq(project.owner)
|
||||
expect(merge_request.metrics.merged_at).to eq(@merge_event.merge_timestamp)
|
||||
expect(merge_request.metrics.merged_by).to eq(pull_request_author)
|
||||
expect(merge_request.metrics.merged_at).to eq(merge_event.merge_timestamp)
|
||||
expect(merge_request.merge_commit_sha).to eq('12345678')
|
||||
expect(merge_request.state_id).to eq(3)
|
||||
end
|
||||
|
||||
it 'imports comments' do
|
||||
expect(subject.client).to receive(:activities).and_return([@pr_comment])
|
||||
describe 'pull request author user mapping' do
|
||||
before do
|
||||
allow(subject.client).to receive(:activities).and_return([merge_event])
|
||||
end
|
||||
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
shared_examples 'imports pull requests' do
|
||||
it 'maps user' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.notes.count).to eq(1)
|
||||
note = merge_request.notes.first
|
||||
expect(note.note).to end_with(@pr_note.note)
|
||||
expect(note.author).to eq(project.owner)
|
||||
expect(note.created_at).to eq(@pr_note.created_at)
|
||||
expect(note.updated_at).to eq(@pr_note.created_at)
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.author).to eq(pull_request_author)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: false)
|
||||
end
|
||||
|
||||
include_examples 'imports pull requests'
|
||||
end
|
||||
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: true)
|
||||
end
|
||||
|
||||
include_examples 'imports pull requests' do
|
||||
context 'when username is not present' do
|
||||
before do
|
||||
allow(pull_request).to receive(:author_username).and_return(nil)
|
||||
end
|
||||
|
||||
it 'maps by email' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.author).to eq(pull_request_author)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not found' do
|
||||
before do
|
||||
allow(pull_request).to receive(:author_username).and_return(nil)
|
||||
allow(pull_request).to receive(:author_email).and_return(nil)
|
||||
end
|
||||
|
||||
it 'maps importer user' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.author).to eq(project_creator)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'comments' do
|
||||
shared_examples 'imports comments' do
|
||||
it 'imports comments' do
|
||||
expect(subject.client).to receive(:activities).and_return([pr_comment])
|
||||
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.notes.count).to eq(1)
|
||||
note = merge_request.notes.first
|
||||
expect(note.note).to end_with(pr_note.note)
|
||||
expect(note.author).to eq(note_author)
|
||||
expect(note.created_at).to eq(pr_note.created_at)
|
||||
expect(note.updated_at).to eq(pr_note.created_at)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: false)
|
||||
end
|
||||
|
||||
include_examples 'imports comments'
|
||||
end
|
||||
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: true)
|
||||
end
|
||||
|
||||
include_examples 'imports comments'
|
||||
|
||||
context 'when username is not present' do
|
||||
before do
|
||||
allow(pr_note).to receive(:author_username).and_return(nil)
|
||||
allow(subject.client).to receive(:activities).and_return([pr_comment])
|
||||
end
|
||||
|
||||
it 'maps by email' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.notes.count).to eq(1)
|
||||
note = merge_request.notes.first
|
||||
expect(note.author).to eq(note_author)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'metrics' do
|
||||
|
|
@ -135,7 +242,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
before do
|
||||
allow(Gitlab::Metrics).to receive(:counter) { counter }
|
||||
allow(Gitlab::Metrics).to receive(:histogram) { histogram }
|
||||
allow(subject.client).to receive(:activities).and_return([@merge_event])
|
||||
allow(subject.client).to receive(:activities).and_return([merge_event])
|
||||
end
|
||||
|
||||
it 'counts and measures duration of imported projects' do
|
||||
|
|
@ -170,73 +277,137 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
end
|
||||
end
|
||||
|
||||
it 'imports threaded discussions' do
|
||||
reply = instance_double(
|
||||
BitbucketServer::Representation::PullRequestComment,
|
||||
author_email: 'someuser@gitlab.com',
|
||||
author_username: 'Batman',
|
||||
note: 'I agree',
|
||||
created_at: now,
|
||||
updated_at: now)
|
||||
describe 'threaded discussions' do
|
||||
let(:reply_author) { create(:user, username: 'reply_author', email: 'reply_author@example.org') }
|
||||
let(:inline_note_author) { create(:user, username: 'inline_note_author', email: 'inline_note_author@example.org') }
|
||||
|
||||
let(:reply) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::PullRequestComment,
|
||||
author_email: reply_author.email,
|
||||
author_username: reply_author.username,
|
||||
note: 'I agree',
|
||||
created_at: now,
|
||||
updated_at: now)
|
||||
end
|
||||
|
||||
# https://gitlab.com/gitlab-org/gitlab-test/compare/c1acaa58bbcbc3eafe538cb8274ba387047b69f8...5937ac0a7beb003549fc5fd26fc247ad
|
||||
inline_note = instance_double(
|
||||
BitbucketServer::Representation::PullRequestComment,
|
||||
file_type: 'ADDED',
|
||||
from_sha: sample.commits.first,
|
||||
to_sha: sample.commits.last,
|
||||
file_path: '.gitmodules',
|
||||
old_pos: nil,
|
||||
new_pos: 4,
|
||||
note: 'Hello world',
|
||||
author_email: 'unknown@gmail.com',
|
||||
author_username: 'Superman',
|
||||
comments: [reply],
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
parent_comment: nil)
|
||||
let(:inline_note) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::PullRequestComment,
|
||||
file_type: 'ADDED',
|
||||
from_sha: sample.commits.first,
|
||||
to_sha: sample.commits.last,
|
||||
file_path: '.gitmodules',
|
||||
old_pos: nil,
|
||||
new_pos: 4,
|
||||
note: 'Hello world',
|
||||
author_email: inline_note_author.email,
|
||||
author_username: inline_note_author.username,
|
||||
comments: [reply],
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
parent_comment: nil)
|
||||
end
|
||||
|
||||
allow(reply).to receive(:parent_comment).and_return(inline_note)
|
||||
let(:inline_comment) do
|
||||
instance_double(
|
||||
BitbucketServer::Representation::Activity,
|
||||
comment?: true,
|
||||
inline_comment?: true,
|
||||
merge_event?: false,
|
||||
comment: inline_note)
|
||||
end
|
||||
|
||||
inline_comment = instance_double(
|
||||
BitbucketServer::Representation::Activity,
|
||||
comment?: true,
|
||||
inline_comment?: true,
|
||||
merge_event?: false,
|
||||
comment: inline_note)
|
||||
before do
|
||||
allow(reply).to receive(:parent_comment).and_return(inline_note)
|
||||
allow(subject.client).to receive(:activities).and_return([inline_comment])
|
||||
end
|
||||
|
||||
expect(subject.client).to receive(:activities).and_return([inline_comment])
|
||||
shared_examples 'imports threaded discussions' do
|
||||
it 'imports threaded discussions' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.notes.count).to eq(2)
|
||||
expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
|
||||
|
||||
merge_request = MergeRequest.first
|
||||
expect(merge_request.notes.count).to eq(2)
|
||||
expect(merge_request.notes.map(&:discussion_id).uniq.count).to eq(1)
|
||||
notes = merge_request.notes.order(:id).to_a
|
||||
start_note = notes.first
|
||||
expect(start_note.type).to eq('DiffNote')
|
||||
expect(start_note.note).to end_with(inline_note.note)
|
||||
expect(start_note.created_at).to eq(inline_note.created_at)
|
||||
expect(start_note.updated_at).to eq(inline_note.updated_at)
|
||||
expect(start_note.position.base_sha).to eq(inline_note.from_sha)
|
||||
expect(start_note.position.start_sha).to eq(inline_note.from_sha)
|
||||
expect(start_note.position.head_sha).to eq(inline_note.to_sha)
|
||||
expect(start_note.position.old_line).to be_nil
|
||||
expect(start_note.position.new_line).to eq(inline_note.new_pos)
|
||||
expect(start_note.author).to eq(inline_note_author)
|
||||
|
||||
notes = merge_request.notes.order(:id).to_a
|
||||
start_note = notes.first
|
||||
expect(start_note.type).to eq('DiffNote')
|
||||
expect(start_note.note).to end_with(inline_note.note)
|
||||
expect(start_note.created_at).to eq(inline_note.created_at)
|
||||
expect(start_note.updated_at).to eq(inline_note.updated_at)
|
||||
expect(start_note.position.base_sha).to eq(inline_note.from_sha)
|
||||
expect(start_note.position.start_sha).to eq(inline_note.from_sha)
|
||||
expect(start_note.position.head_sha).to eq(inline_note.to_sha)
|
||||
expect(start_note.position.old_line).to be_nil
|
||||
expect(start_note.position.new_line).to eq(inline_note.new_pos)
|
||||
reply_note = notes.last
|
||||
# Make sure author and reply context is included
|
||||
expect(reply_note.note).to start_with("> #{inline_note.note}\n\n#{reply.note}")
|
||||
expect(reply_note.author).to eq(reply_author)
|
||||
expect(reply_note.created_at).to eq(reply.created_at)
|
||||
expect(reply_note.updated_at).to eq(reply.created_at)
|
||||
expect(reply_note.position.base_sha).to eq(inline_note.from_sha)
|
||||
expect(reply_note.position.start_sha).to eq(inline_note.from_sha)
|
||||
expect(reply_note.position.head_sha).to eq(inline_note.to_sha)
|
||||
expect(reply_note.position.old_line).to be_nil
|
||||
expect(reply_note.position.new_line).to eq(inline_note.new_pos)
|
||||
end
|
||||
end
|
||||
|
||||
reply_note = notes.last
|
||||
# Make sure author and reply context is included
|
||||
expect(reply_note.note).to start_with("*By #{reply.author_username} (#{reply.author_email})*\n\n")
|
||||
expect(reply_note.note).to end_with("> #{inline_note.note}\n\n#{reply.note}")
|
||||
expect(reply_note.author).to eq(project.owner)
|
||||
expect(reply_note.created_at).to eq(reply.created_at)
|
||||
expect(reply_note.updated_at).to eq(reply.created_at)
|
||||
expect(reply_note.position.base_sha).to eq(inline_note.from_sha)
|
||||
expect(reply_note.position.start_sha).to eq(inline_note.from_sha)
|
||||
expect(reply_note.position.head_sha).to eq(inline_note.to_sha)
|
||||
expect(reply_note.position.old_line).to be_nil
|
||||
expect(reply_note.position.new_line).to eq(inline_note.new_pos)
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: false)
|
||||
end
|
||||
|
||||
include_examples 'imports threaded discussions'
|
||||
end
|
||||
|
||||
context 'when bitbucket_server_user_mapping_by_username feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(bitbucket_server_user_mapping_by_username: true)
|
||||
end
|
||||
|
||||
include_examples 'imports threaded discussions' do
|
||||
context 'when username is not present' do
|
||||
before do
|
||||
allow(reply).to receive(:author_username).and_return(nil)
|
||||
allow(inline_note).to receive(:author_username).and_return(nil)
|
||||
end
|
||||
|
||||
it 'maps by email' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
notes = MergeRequest.first.notes.order(:id).to_a
|
||||
|
||||
expect(notes.first.author).to eq(inline_note_author)
|
||||
expect(notes.last.author).to eq(reply_author)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not found' do
|
||||
before do
|
||||
allow(reply).to receive(:author_username).and_return(nil)
|
||||
allow(reply).to receive(:author_email).and_return(nil)
|
||||
allow(inline_note).to receive(:author_username).and_return(nil)
|
||||
allow(inline_note).to receive(:author_email).and_return(nil)
|
||||
end
|
||||
|
||||
it 'maps importer user' do
|
||||
expect { subject.execute }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
notes = MergeRequest.first.notes.order(:id).to_a
|
||||
|
||||
expect(notes.first.author).to eq(project_creator)
|
||||
expect(notes.last.author).to eq(project_creator)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'falls back to comments if diff comments fail to validate' do
|
||||
|
|
@ -312,6 +483,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
state: 'merged',
|
||||
author: 'Test Author',
|
||||
author_email: project.owner.email,
|
||||
author_username: 'author',
|
||||
created_at: Time.now,
|
||||
updated_at: Time.now,
|
||||
merged?: true)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
it 'contains node level metrics for each instance' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query,
|
||||
receive_query_apdex_ratio_query,
|
||||
receive_node_memory_query,
|
||||
receive_node_memory_utilization_query,
|
||||
receive_node_cpu_count_query,
|
||||
|
|
@ -33,6 +34,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
expect(subject[:topology]).to eq({
|
||||
duration_s: 0,
|
||||
application_requests_per_hour: 36,
|
||||
query_apdex_weekly_average: 0.996,
|
||||
failures: [],
|
||||
nodes: [
|
||||
{
|
||||
|
|
@ -102,6 +104,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
it 'removes the respective entries and includes the failures' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query(result: []),
|
||||
receive_query_apdex_ratio_query(result: []),
|
||||
receive_node_memory_query(result: []),
|
||||
receive_node_memory_utilization_query(result: []),
|
||||
receive_node_cpu_count_query,
|
||||
|
|
@ -118,6 +121,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'empty_result' },
|
||||
{ 'query_apdex' => 'empty_result' },
|
||||
{ 'node_memory' => 'empty_result' },
|
||||
{ 'node_memory_utilization' => 'empty_result' },
|
||||
{ 'service_rss' => 'empty_result' },
|
||||
|
|
@ -240,6 +244,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
it 'normalizes equivalent instance values and maps them to the same node' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query(result: []),
|
||||
receive_query_apdex_ratio_query(result: []),
|
||||
receive_node_memory_query(result: node_memory_response),
|
||||
receive_node_memory_utilization_query(result: node_memory_utilization_response),
|
||||
receive_node_cpu_count_query(result: []),
|
||||
|
|
@ -256,6 +261,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'empty_result' },
|
||||
{ 'query_apdex' => 'empty_result' },
|
||||
{ 'node_cpus' => 'empty_result' },
|
||||
{ 'node_cpu_utilization' => 'empty_result' },
|
||||
{ 'service_uss' => 'empty_result' },
|
||||
|
|
@ -304,6 +310,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
it 'still reports service metrics' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query(result: []),
|
||||
receive_query_apdex_ratio_query(result: []),
|
||||
receive_node_memory_query(result: []),
|
||||
receive_node_memory_utilization_query(result: []),
|
||||
receive_node_cpu_count_query(result: []),
|
||||
|
|
@ -320,6 +327,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'empty_result' },
|
||||
{ 'query_apdex' => 'empty_result' },
|
||||
{ 'node_memory' => 'empty_result' },
|
||||
{ 'node_memory_utilization' => 'empty_result' },
|
||||
{ 'node_cpus' => 'empty_result' },
|
||||
|
|
@ -377,6 +385,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
it 'filters out unknown service data and reports the unknown services as a failure' do
|
||||
expect_prometheus_api_to(
|
||||
receive_app_request_volume_query(result: []),
|
||||
receive_query_apdex_ratio_query(result: []),
|
||||
receive_node_memory_query(result: []),
|
||||
receive_node_memory_utilization_query(result: []),
|
||||
receive_node_cpu_count_query(result: []),
|
||||
|
|
@ -407,6 +416,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'query_apdex' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'node_memory' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'node_memory_utilization' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
{ 'node_cpus' => 'Gitlab::PrometheusClient::ConnectionError' },
|
||||
|
|
@ -437,6 +447,7 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
duration_s: 0,
|
||||
failures: [
|
||||
{ 'app_requests' => exception.to_s },
|
||||
{ 'query_apdex' => 'timeout_cancellation' },
|
||||
{ 'node_memory' => 'timeout_cancellation' },
|
||||
{ 'node_memory_utilization' => 'timeout_cancellation' },
|
||||
{ 'node_cpus' => 'timeout_cancellation' },
|
||||
|
|
@ -512,6 +523,17 @@ RSpec.describe Gitlab::UsageData::Topology do
|
|||
])
|
||||
end
|
||||
|
||||
def receive_query_apdex_ratio_query(result: nil)
|
||||
receive(:query)
|
||||
.with(/gitlab_usage_ping:sql_duration_apdex:ratio_rate5m/)
|
||||
.and_return(result || [
|
||||
{
|
||||
'metric' => {},
|
||||
'value' => [1000, '0.996']
|
||||
}
|
||||
])
|
||||
end
|
||||
|
||||
def receive_node_memory_query(result: nil)
|
||||
receive(:query)
|
||||
.with(/node_memory_total_bytes/, an_instance_of(Hash))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
let_it_be(:reporter) { create(:user) }
|
||||
let_it_be(:stranger) { create(:user) }
|
||||
let_it_be(:link_filepath) { '/direct/asset/link/path' }
|
||||
let_it_be(:released_at) { Time.now - 1.day }
|
||||
|
||||
let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
|
||||
let(:post_query) { post_graphql(query, current_user: current_user) }
|
||||
|
|
@ -39,6 +40,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
name
|
||||
createdAt
|
||||
releasedAt
|
||||
upcomingRelease
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -54,7 +56,8 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
'descriptionHtml' => release.description_html,
|
||||
'name' => release.name,
|
||||
'createdAt' => release.created_at.iso8601,
|
||||
'releasedAt' => release.released_at.iso8601
|
||||
'releasedAt' => release.released_at.iso8601,
|
||||
'upcomingRelease' => false
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
@ -270,7 +273,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
let_it_be(:project) { create(:project, :repository, :private) }
|
||||
let_it_be(:milestone_1) { create(:milestone, project: project) }
|
||||
let_it_be(:milestone_2) { create(:milestone, project: project) }
|
||||
let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2]) }
|
||||
let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) }
|
||||
let_it_be(:release_link_1) { create(:release_link, release: release) }
|
||||
let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) }
|
||||
|
||||
|
|
@ -311,7 +314,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
let_it_be(:project) { create(:project, :repository, :public) }
|
||||
let_it_be(:milestone_1) { create(:milestone, project: project) }
|
||||
let_it_be(:milestone_2) { create(:milestone, project: project) }
|
||||
let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2]) }
|
||||
let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) }
|
||||
let_it_be(:release_link_1) { create(:release_link, release: release) }
|
||||
let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) }
|
||||
|
||||
|
|
@ -373,4 +376,45 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
|
|||
it_behaves_like 'no access to the release field'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'upcoming release' do
|
||||
let(:path) { path_prefix }
|
||||
let(:project) { create(:project, :repository, :private) }
|
||||
let(:release) { create(:release, :with_evidence, project: project, released_at: released_at) }
|
||||
let(:current_user) { developer }
|
||||
|
||||
let(:release_fields) do
|
||||
query_graphql_field(%{
|
||||
releasedAt
|
||||
upcomingRelease
|
||||
})
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(developer)
|
||||
post_query
|
||||
end
|
||||
|
||||
context 'future release' do
|
||||
let(:released_at) { Time.now + 1.day }
|
||||
|
||||
it 'finds all release data' do
|
||||
expect(data).to eq({
|
||||
'releasedAt' => release.released_at.iso8601,
|
||||
'upcomingRelease' => true
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context 'past release' do
|
||||
let(:released_at) { Time.now - 1.day }
|
||||
|
||||
it 'finds all release data' do
|
||||
expect(data).to eq({
|
||||
'releasedAt' => release.released_at.iso8601,
|
||||
'upcomingRelease' => false
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -82,6 +82,30 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
|
|||
expect(user).to have_received(:can?).with(:create_issue, project)
|
||||
end
|
||||
|
||||
context 'with alert severity' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:alert_severity, :incident_severity) do
|
||||
'critical' | 'critical'
|
||||
'high' | 'high'
|
||||
'medium' | 'medium'
|
||||
'low' | 'low'
|
||||
'info' | 'unknown'
|
||||
'unknown' | 'unknown'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
alert.update!(severity: alert_severity)
|
||||
execute
|
||||
end
|
||||
|
||||
it 'sets the correct severity level' do
|
||||
expect(created_issue.severity).to eq(incident_severity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the alert is prometheus alert' do
|
||||
let(:alert) { prometheus_alert }
|
||||
let(:issue) { subject.payload[:issue] }
|
||||
|
|
|
|||
|
|
@ -39,6 +39,34 @@ RSpec.describe IncidentManagement::Incidents::CreateService do
|
|||
let(:issue) { new_issue }
|
||||
end
|
||||
|
||||
context 'with default severity' do
|
||||
it 'sets the correct severity level to "unknown"' do
|
||||
create_incident
|
||||
expect(new_issue.severity).to eq(IssuableSeverity::DEFAULT)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with severity' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
subject(:create_incident) { described_class.new(project, user, title: title, description: description, severity: severity).execute }
|
||||
|
||||
where(:severity, :incident_severity) do
|
||||
'critical' | 'critical'
|
||||
'high' | 'high'
|
||||
'medium' | 'medium'
|
||||
'low' | 'low'
|
||||
'unknown' | 'unknown'
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'sets the correct severity level' do
|
||||
create_incident
|
||||
expect(new_issue.severity).to eq(incident_severity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when incident label does not exists' do
|
||||
it 'creates incident label' do
|
||||
expect { create_incident }.to change { project.labels.where(title: label_title).count }.by(1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue