Add latest changes from gitlab-org/gitlab@master
|
|
@ -584,6 +584,8 @@
|
|||
rules:
|
||||
- <<: *if-not-ee
|
||||
when: never
|
||||
- <<: *if-merge-request
|
||||
changes: *code-backstage-patterns
|
||||
- <<: *if-master-schedule-2-hourly
|
||||
- <<: *if-merge-request-title-run-all-rspec
|
||||
|
||||
|
|
|
|||
1
Gemfile
|
|
@ -45,6 +45,7 @@ gem 'omniauth_crowd', '~> 2.4.0'
|
|||
gem 'omniauth-authentiq', '~> 0.3.3'
|
||||
gem 'omniauth_openid_connect', '~> 0.3.5'
|
||||
gem 'omniauth-salesforce', '~> 1.0.5'
|
||||
gem 'omniauth-atlassian-oauth2', '~> 0.2.0'
|
||||
gem 'rack-oauth2', '~> 1.9.3'
|
||||
gem 'jwt', '~> 2.1.0'
|
||||
|
||||
|
|
|
|||
|
|
@ -737,6 +737,9 @@ GEM
|
|||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-atlassian-oauth2 (0.2.0)
|
||||
omniauth (>= 1.1.1)
|
||||
omniauth-oauth2 (>= 1.5)
|
||||
omniauth-auth0 (2.0.0)
|
||||
omniauth-oauth2 (~> 1.4)
|
||||
omniauth-authentiq (0.3.3)
|
||||
|
|
@ -1393,6 +1396,7 @@ DEPENDENCIES
|
|||
octokit (~> 4.15)
|
||||
oj (~> 3.10.6)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-atlassian-oauth2 (~> 0.2.0)
|
||||
omniauth-auth0 (~> 2.0.0)
|
||||
omniauth-authentiq (~> 0.3.3)
|
||||
omniauth-azure-oauth2 (~> 0.0.9)
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,12 @@
|
|||
query getHighlightBarInfo($iid: String!, $fullPath: ID!) {
|
||||
project(fullPath: $fullPath) {
|
||||
issue(iid: $iid) {
|
||||
alertManagementAlert {
|
||||
title
|
||||
detailsUrl
|
||||
createdAt
|
||||
eventCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<script>
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
import getHighlightBarInfo from './graphql/queries/get_highlight_bar_info.graphql';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlLink,
|
||||
},
|
||||
inject: ['fullPath', 'iid'],
|
||||
apollo: {
|
||||
alert: {
|
||||
query: getHighlightBarInfo,
|
||||
variables() {
|
||||
return {
|
||||
fullPath: this.fullPath,
|
||||
iid: this.iid,
|
||||
};
|
||||
},
|
||||
update: data => data.project?.issue?.alertManagementAlert,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
startTime() {
|
||||
return formatDate(this.alert.createdAt, 'yyyy-mm-dd Z');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="alert"
|
||||
class="gl-border-solid gl-border-1 gl-border-gray-100 gl-p-5 gl-mb-3 gl-rounded-base gl-display-flex gl-justify-content-space-between"
|
||||
>
|
||||
<div class="text-truncate gl-pr-3">
|
||||
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Original alert:') }}</span>
|
||||
<gl-link :href="alert.detailsUrl">{{ alert.title }}</gl-link>
|
||||
</div>
|
||||
|
||||
<div class="gl-pr-3 gl-white-space-nowrap">
|
||||
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert start time:') }}</span>
|
||||
{{ startTime }}
|
||||
</div>
|
||||
|
||||
<div class="gl-white-space-nowrap">
|
||||
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert events:') }}</span>
|
||||
<span>{{ alert.eventCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,24 +1,23 @@
|
|||
<script>
|
||||
import { GlTab, GlTabs } from '@gitlab/ui';
|
||||
import DescriptionComponent from './description.vue';
|
||||
import DescriptionComponent from '../description.vue';
|
||||
import HighlightBar from './highlight_bar/higlight_bar.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlTab,
|
||||
GlTabs,
|
||||
DescriptionComponent,
|
||||
HighlightBar,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-tabs
|
||||
content-class="gl-reset-line-height gl-mt-3"
|
||||
class="gl-mt-n3"
|
||||
data-testid="incident-tabs"
|
||||
>
|
||||
<gl-tabs content-class="gl-reset-line-height" class="gl-mt-n3" data-testid="incident-tabs">
|
||||
<gl-tab :title="__('Summary')">
|
||||
<highlight-bar />
|
||||
<description-component v-bind="$attrs" />
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
|
|
@ -1,13 +1,28 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import issuableApp from './components/app.vue';
|
||||
import incidentTabs from './components/incident_tabs.vue';
|
||||
import incidentTabs from './components/incidents/incident_tabs.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export default function initIssuableApp(issuableData = {}) {
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
const { projectNamespace, projectPath, iid } = issuableData;
|
||||
|
||||
return new Vue({
|
||||
el: document.getElementById('js-issuable-app'),
|
||||
apolloProvider,
|
||||
components: {
|
||||
issuableApp,
|
||||
},
|
||||
provide: {
|
||||
fullPath: `${projectNamespace}/${projectPath}`,
|
||||
iid,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('issuable-app', {
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import UserCallout from '~/user_callout';
|
||||
import UsagePingDisabled from '~/admin/dev_ops_score/components/usage_ping_disabled.vue';
|
||||
import UsagePingDisabled from '~/admin/dev_ops_report/components/usage_ping_disabled.vue';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// eslint-disable-next-line no-new
|
||||
|
|
@ -2,13 +2,11 @@
|
|||
import { mapGetters } from 'vuex';
|
||||
import { GlTooltipDirective, GlFriendlyWrap, GlIcon } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||
|
||||
export default {
|
||||
name: 'TestsSuiteTable',
|
||||
components: {
|
||||
GlIcon,
|
||||
SmartVirtualList,
|
||||
GlFriendlyWrap,
|
||||
},
|
||||
directives: {
|
||||
|
|
@ -27,8 +25,6 @@ export default {
|
|||
return this.getSuiteTests.length > 0;
|
||||
},
|
||||
},
|
||||
maxShownRows: 30,
|
||||
typicalRowHeight: 75,
|
||||
wrapSymbols: ['::', '#', '.', '_', '-', '/', '\\'],
|
||||
};
|
||||
</script>
|
||||
|
|
@ -60,66 +56,60 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<smart-virtual-list
|
||||
:length="getSuiteTests.length"
|
||||
:remain="$options.maxShownRows"
|
||||
:size="$options.typicalRowHeight"
|
||||
<div
|
||||
v-for="(testCase, index) in getSuiteTests"
|
||||
:key="index"
|
||||
class="gl-responsive-table-row rounded align-items-md-start mt-xs-3 js-case-row"
|
||||
>
|
||||
<div
|
||||
v-for="(testCase, index) in getSuiteTests"
|
||||
:key="index"
|
||||
class="gl-responsive-table-row rounded align-items-md-start mt-xs-3 js-case-row"
|
||||
>
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Suite') }}</div>
|
||||
<div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
|
||||
<gl-friendly-wrap :symbols="$options.wrapSymbols" :text="testCase.classname" />
|
||||
</div>
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Suite') }}</div>
|
||||
<div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
|
||||
<gl-friendly-wrap :symbols="$options.wrapSymbols" :text="testCase.classname" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
||||
<div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
|
||||
<gl-friendly-wrap
|
||||
data-testid="caseName"
|
||||
:symbols="$options.wrapSymbols"
|
||||
:text="testCase.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
||||
<div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
|
||||
<gl-friendly-wrap
|
||||
data-testid="caseName"
|
||||
:symbols="$options.wrapSymbols"
|
||||
:text="testCase.name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||
<div class="table-mobile-content text-center">
|
||||
<div
|
||||
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||
:class="`ci-status-icon-${testCase.status}`"
|
||||
>
|
||||
<gl-icon :size="24" :name="testCase.icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section flex-grow-1">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
|
||||
<div class="table-mobile-content">
|
||||
<pre
|
||||
v-if="testCase.system_output"
|
||||
class="build-trace build-trace-rounded text-left"
|
||||
><code class="bash p-0">{{testCase.system_output}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-right pr-sm-1">
|
||||
{{ testCase.formattedTime }}
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||
<div class="table-mobile-content text-center">
|
||||
<div
|
||||
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||
:class="`ci-status-icon-${testCase.status}`"
|
||||
>
|
||||
<gl-icon :size="24" :name="testCase.icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</smart-virtual-list>
|
||||
|
||||
<div class="table-section flex-grow-1">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
|
||||
<div class="table-mobile-content">
|
||||
<pre
|
||||
v-if="testCase.system_output"
|
||||
class="build-trace build-trace-rounded text-left"
|
||||
><code class="bash p-0">{{testCase.system_output}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-right pr-sm-1">
|
||||
{{ testCase.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
import { mapGetters } from 'vuex';
|
||||
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||
|
||||
export default {
|
||||
name: 'TestsSummaryTable',
|
||||
components: {
|
||||
GlIcon,
|
||||
SmartVirtualList,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -31,8 +29,6 @@ export default {
|
|||
this.$emit('row-click', index);
|
||||
},
|
||||
},
|
||||
maxShownRows: 20,
|
||||
typicalRowHeight: 55,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -69,83 +65,77 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<smart-virtual-list
|
||||
:length="getTestSuites.length"
|
||||
:remain="$options.maxShownRows"
|
||||
:size="$options.typicalRowHeight"
|
||||
<div
|
||||
v-for="(testSuite, index) in getTestSuites"
|
||||
:key="index"
|
||||
role="row"
|
||||
class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
|
||||
:class="{
|
||||
'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
|
||||
}"
|
||||
@click="tableRowClick(index)"
|
||||
>
|
||||
<div
|
||||
v-for="(testSuite, index) in getTestSuites"
|
||||
:key="index"
|
||||
role="row"
|
||||
class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
|
||||
:class="{
|
||||
'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
|
||||
}"
|
||||
@click="tableRowClick(index)"
|
||||
>
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Suite') }}
|
||||
</div>
|
||||
<div class="table-mobile-content underline cgray pl-3">
|
||||
{{ testSuite.name }}
|
||||
<gl-icon
|
||||
v-if="testSuite.suite_error"
|
||||
ref="suiteErrorIcon"
|
||||
v-gl-tooltip
|
||||
name="error"
|
||||
:title="testSuite.suite_error"
|
||||
class="vertical-align-middle"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Suite') }}
|
||||
</div>
|
||||
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-md-left">
|
||||
{{ testSuite.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Failed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Errors') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Skipped') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Passed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Total') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||
<div class="table-mobile-content underline cgray pl-3">
|
||||
{{ testSuite.name }}
|
||||
<gl-icon
|
||||
v-if="testSuite.suite_error"
|
||||
ref="suiteErrorIcon"
|
||||
v-gl-tooltip
|
||||
name="error"
|
||||
:title="testSuite.suite_error"
|
||||
class="vertical-align-middle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</smart-virtual-list>
|
||||
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-md-left">
|
||||
{{ testSuite.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Failed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Errors') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Skipped') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Passed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Total') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import { __, s__ } from '~/locale';
|
||||
|
||||
export const INCIDENT_SEVERITY = {
|
||||
CRITICAL: {
|
||||
value: 'CRITICAL',
|
||||
icon: 'critical',
|
||||
label: s__('IncidentManagement|Critical - S1'),
|
||||
},
|
||||
HIGH: {
|
||||
value: 'HIGH',
|
||||
icon: 'high',
|
||||
label: s__('IncidentManagement|High - S2'),
|
||||
},
|
||||
MEDIUM: {
|
||||
value: 'MEDIUM',
|
||||
icon: 'medium',
|
||||
label: s__('IncidentManagement|Medium - S3'),
|
||||
},
|
||||
LOW: {
|
||||
value: 'LOW',
|
||||
icon: 'low',
|
||||
label: s__('IncidentManagement|Low - S4'),
|
||||
},
|
||||
UNKNOWN: {
|
||||
value: 'UNKNOWN',
|
||||
icon: 'unknown',
|
||||
label: s__('IncidentManagement|Unknown'),
|
||||
},
|
||||
};
|
||||
|
||||
export const ISSUABLE_TYPES = {
|
||||
INCIDENT: 'incident',
|
||||
};
|
||||
|
||||
export const SIDEBAR_ANIMATION_DURATION = 300;
|
||||
|
||||
export const I18N = {
|
||||
UPDATE_SEVERITY_ERROR: s__('SeverityWidget|There was an error while updating severity.'),
|
||||
TRY_AGAIN: __('Please try again'),
|
||||
EDIT: __('Edit'),
|
||||
SEVERITY: s__('SeverityWidget|Severity'),
|
||||
SEVERITY_VALUE: s__('SeverityWidget|Severity: %{severity}'),
|
||||
};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
mutation updateIssuableSeverity($projectPath: ID!, $severity: IssuableSeverity!, $iid: String!) {
|
||||
issueSetSeverity(input: { iid: $iid, severity: $severity, projectPath: $projectPath }) {
|
||||
errors
|
||||
issue {
|
||||
iid
|
||||
severity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
severity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator(severity) {
|
||||
const { value, label, icon } = severity;
|
||||
return value && label && icon;
|
||||
},
|
||||
},
|
||||
iconSize: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 12,
|
||||
},
|
||||
iconOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="incident-severity gl-display-inline-flex gl-align-items-center gl-justify-content-between"
|
||||
>
|
||||
<gl-icon
|
||||
:size="iconSize"
|
||||
:name="`severity-${severity.icon}`"
|
||||
:class="[`icon-${severity.icon}`, { 'gl-mr-3': !iconOnly }]"
|
||||
/>
|
||||
<span v-if="!iconOnly">{{ severity.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
<script>
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlLoadingIcon,
|
||||
GlTooltip,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
} from '@gitlab/ui';
|
||||
import { INCIDENT_SEVERITY, ISSUABLE_TYPES, SIDEBAR_ANIMATION_DURATION, I18N } from './constants';
|
||||
import updateIssuableSeverity from './graphql/mutations/update_issuable_severity.mutation.graphql';
|
||||
import SeverityToken from './severity.vue';
|
||||
import createFlash from '~/flash';
|
||||
|
||||
export default {
|
||||
i18n: I18N,
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlTooltip,
|
||||
GlSprintf,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlLink,
|
||||
SeverityToken,
|
||||
},
|
||||
props: {
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
iid: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
initialSeverity: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: INCIDENT_SEVERITY.UNKNOWN.value,
|
||||
},
|
||||
issuableType: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ISSUABLE_TYPES.INCIDENT,
|
||||
validator: value => {
|
||||
// currently severity is supported only for incidents, but this list might be extended
|
||||
return [ISSUABLE_TYPES.INCIDENT].includes(value);
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isDropdownShowing: false,
|
||||
isUpdating: false,
|
||||
severity: this.initialSeverity,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
severitiesList() {
|
||||
switch (this.issuableType) {
|
||||
case ISSUABLE_TYPES.INCIDENT:
|
||||
return Object.values(INCIDENT_SEVERITY);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
},
|
||||
dropdownClass() {
|
||||
return this.isDropdownShowing ? 'show' : 'gl-display-none';
|
||||
},
|
||||
selectedItem() {
|
||||
return this.severitiesList.find(severity => severity.value === this.severity);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.handleOffClick);
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', this.handleOffClick);
|
||||
},
|
||||
methods: {
|
||||
handleOffClick(event) {
|
||||
if (!this.isDropdownShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.$refs.sidebarSeverity.contains(event.target)) {
|
||||
this.hideDropdown();
|
||||
}
|
||||
},
|
||||
hideDropdown() {
|
||||
this.isDropdownShowing = false;
|
||||
const event = new Event('hidden.gl.dropdown');
|
||||
this.$el.dispatchEvent(event);
|
||||
},
|
||||
toggleFormDropdown(collapsedSidebar) {
|
||||
this.isDropdownShowing = !this.isDropdownShowing;
|
||||
const timeout = collapsedSidebar ? SIDEBAR_ANIMATION_DURATION : 0;
|
||||
setTimeout(() => {
|
||||
const { dropdown } = this.$refs;
|
||||
if (dropdown && this.isDropdownShowing) {
|
||||
dropdown.show();
|
||||
}
|
||||
}, timeout);
|
||||
},
|
||||
updateSeverity(value) {
|
||||
this.hideDropdown();
|
||||
this.isUpdating = true;
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: updateIssuableSeverity,
|
||||
variables: {
|
||||
iid: this.iid,
|
||||
severity: value,
|
||||
projectPath: this.projectPath,
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const {
|
||||
data: {
|
||||
issueSetSeverity: {
|
||||
errors = [],
|
||||
issue: { severity },
|
||||
},
|
||||
},
|
||||
} = resp;
|
||||
|
||||
if (errors[0]) {
|
||||
throw errors[0];
|
||||
}
|
||||
this.severity = severity;
|
||||
})
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
message: `${this.$options.i18n.UPDATE_SEVERITY_ERROR} ${this.$options.i18n.TRY_AGAIN}`,
|
||||
}),
|
||||
)
|
||||
.finally(() => {
|
||||
this.isUpdating = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="sidebarSeverity" class="block">
|
||||
<div ref="severity" class="sidebar-collapsed-icon" @click="toggleFormDropdown(true)">
|
||||
<severity-token :severity="selectedItem" :icon-size="14" :icon-only="true" />
|
||||
<gl-tooltip :target="() => $refs.severity" boundary="viewport" placement="left">
|
||||
<gl-sprintf :message="$options.i18n.SEVERITY_VALUE">
|
||||
<template #severity>
|
||||
{{ selectedItem.label }}
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="hide-collapsed">
|
||||
<p class="title gl-display-flex gl-justify-content-space-between">
|
||||
{{ $options.i18n.SEVERITY }}
|
||||
<gl-link
|
||||
data-testid="editButton"
|
||||
href="#"
|
||||
@click="toggleFormDropdown"
|
||||
@keydown.esc="hideDropdown"
|
||||
>
|
||||
{{ $options.i18n.EDIT }}
|
||||
</gl-link>
|
||||
</p>
|
||||
|
||||
<gl-dropdown
|
||||
ref="dropdown"
|
||||
:class="dropdownClass"
|
||||
block
|
||||
:text="selectedItem.label"
|
||||
toggle-class="dropdown-menu-toggle gl-mb-2"
|
||||
@keydown.esc.native="hideDropdown"
|
||||
>
|
||||
<gl-dropdown-item
|
||||
v-for="option in severitiesList"
|
||||
:key="option.value"
|
||||
data-testid="severityDropdownItem"
|
||||
:is-check-item="true"
|
||||
:is-checked="option.value === severity"
|
||||
@click="updateSeverity(option.value)"
|
||||
>
|
||||
<severity-token :severity="option" />
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
|
||||
<gl-loading-icon v-if="isUpdating" :inline="true" />
|
||||
|
||||
<severity-token v-else-if="!isDropdownShowing" :severity="selectedItem" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -8,6 +8,7 @@ import SidebarMoveIssue from './lib/sidebar_move_issue';
|
|||
import IssuableLockForm from './components/lock/issuable_lock_form.vue';
|
||||
import sidebarParticipants from './components/participants/sidebar_participants.vue';
|
||||
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
|
||||
import SidebarSeverity from './components/severity/sidebar_severity.vue';
|
||||
import Translate from '../vue_shared/translate';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { store } from '~/notes/stores';
|
||||
|
|
@ -159,6 +160,35 @@ function mountTimeTrackingComponent() {
|
|||
});
|
||||
}
|
||||
|
||||
function mountSeverityComponent() {
|
||||
const severityContainerEl = document.querySelector('#js-severity');
|
||||
|
||||
if (!severityContainerEl) {
|
||||
return false;
|
||||
}
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
const { fullPath, iid, severity } = getSidebarOptions();
|
||||
|
||||
return new Vue({
|
||||
el: severityContainerEl,
|
||||
apolloProvider,
|
||||
components: {
|
||||
SidebarSeverity,
|
||||
},
|
||||
render: createElement =>
|
||||
createElement('sidebar-severity', {
|
||||
props: {
|
||||
projectPath: fullPath,
|
||||
iid: String(iid),
|
||||
initialSeverity: severity.toUpperCase(),
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export function mountSidebar(mediator) {
|
||||
mountAssigneesComponent(mediator);
|
||||
mountConfidentialComponent(mediator);
|
||||
|
|
@ -173,6 +203,8 @@ export function mountSidebar(mediator) {
|
|||
).init();
|
||||
|
||||
mountTimeTrackingComponent();
|
||||
|
||||
mountSeverityComponent();
|
||||
}
|
||||
|
||||
export { getSidebarOptions };
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
@import './pages/cycle_analytics';
|
||||
@import './pages/deploy_keys';
|
||||
@import './pages/detail_page';
|
||||
@import './pages/dev_ops_score';
|
||||
@import './pages/dev_ops_report';
|
||||
@import './pages/diff';
|
||||
@import './pages/editor';
|
||||
@import './pages/environment_logs';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
.incident-severity,
|
||||
.incident-management-list,
|
||||
.alert-management-details {
|
||||
.icon-critical {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::DevOpsScoreController < Admin::ApplicationController
|
||||
class Admin::DevOpsReportController < Admin::ApplicationController
|
||||
include Analytics::UniqueVisitsHelper
|
||||
|
||||
track_unique_visits :show, target_id: 'i_analytics_dev_ops_score'
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def show
|
||||
@metric = DevOpsScore::Metric.order(:created_at).last&.present
|
||||
@metric = DevOpsReport::Metric.order(:created_at).last&.present
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
|
@ -19,7 +19,7 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
# the Group with statistics).
|
||||
@group = Group.with_statistics.find(group&.id)
|
||||
@members = present_members(
|
||||
@group.members.order("access_level DESC").page(params[:members_page]))
|
||||
group_members.order("access_level DESC").page(params[:members_page]))
|
||||
@requesters = present_members(
|
||||
AccessRequestsFinder.new(@group).execute(current_user))
|
||||
@projects = @group.projects.with_statistics.page(params[:projects_page])
|
||||
|
|
@ -82,6 +82,10 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
@group ||= Group.find_by_full_path(params[:id])
|
||||
end
|
||||
|
||||
def group_members
|
||||
@group.members
|
||||
end
|
||||
|
||||
def group_params
|
||||
params.require(:group).permit(allowed_group_params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
end
|
||||
end
|
||||
|
||||
def atlassian_oauth2
|
||||
omniauth_flow(Gitlab::Auth::Atlassian)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_failed_login(user, provider)
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@ class Profiles::AccountsController < Profiles::ApplicationController
|
|||
render(locals: show_view_variables)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def unlink
|
||||
provider = params[:provider]
|
||||
identity = current_user.identities.find_by(provider: provider)
|
||||
identity = find_identity(provider)
|
||||
|
||||
return render_404 unless identity
|
||||
|
||||
|
|
@ -22,13 +21,18 @@ class Profiles::AccountsController < Profiles::ApplicationController
|
|||
|
||||
redirect_to profile_account_path
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
def show_view_variables
|
||||
{}
|
||||
end
|
||||
|
||||
def find_identity(provider)
|
||||
return current_user.atlassian_identity if provider == 'atlassian_oauth2'
|
||||
|
||||
current_user.identities.find_by(provider: provider) # rubocop: disable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
Profiles::AccountsController.prepend_if_ee('EE::Profiles::AccountsController')
|
||||
|
|
|
|||
|
|
@ -14,7 +14,13 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@config = Gitlab::StaticSiteEditor::Config.new(@repository, @ref, @path, params[:return_url])
|
||||
config = Gitlab::StaticSiteEditor::Config::CombinedConfig.new(
|
||||
@repository,
|
||||
@ref,
|
||||
@path,
|
||||
params[:return_url]
|
||||
)
|
||||
@data = config.data
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class GroupMembersFinder < UnionFinder
|
|||
relations << group_members if include_relations.include?(:direct)
|
||||
|
||||
if include_relations.include?(:inherited) && group.parent
|
||||
parents_members = GroupMember.non_request
|
||||
parents_members = GroupMember.non_request.non_minimal_access
|
||||
.where(source_id: group.ancestors.select(:id))
|
||||
.where.not(user_id: group.users.select(:id))
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class GroupMembersFinder < UnionFinder
|
|||
end
|
||||
|
||||
if include_relations.include?(:descendants)
|
||||
descendant_members = GroupMember.non_request
|
||||
descendant_members = GroupMember.non_request.non_minimal_access
|
||||
.where(source_id: group.descendants.select(:id))
|
||||
.where.not(user_id: group.users.select(:id))
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class MembersFinder
|
|||
def direct_group_members(include_descendants)
|
||||
requested_relations = [:inherited, :direct]
|
||||
requested_relations << :descendants if include_descendants
|
||||
GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite # rubocop: disable CodeReuse/Finder
|
||||
GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite.non_minimal_access # rubocop: disable CodeReuse/Finder
|
||||
end
|
||||
|
||||
def project_invited_groups_members
|
||||
|
|
@ -73,7 +73,7 @@ class MembersFinder
|
|||
.public_or_visible_to_user(current_user)
|
||||
.select(:id)
|
||||
|
||||
GroupMember.with_source_id(invited_groups_ids_including_ancestors)
|
||||
GroupMember.with_source_id(invited_groups_ids_including_ancestors).non_minimal_access
|
||||
end
|
||||
|
||||
def distinct_union_of_members(union_members)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AuthHelper
|
||||
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce).freeze
|
||||
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce atlassian_oauth2).freeze
|
||||
LDAP_PROVIDER = /\Aldap/.freeze
|
||||
|
||||
def ldap_enabled?
|
||||
|
|
@ -133,6 +133,8 @@ module AuthHelper
|
|||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def auth_active?(provider)
|
||||
return current_user.atlassian_identity.present? if provider == :atlassian_oauth2
|
||||
|
||||
current_user.identities.exists?(provider: provider.to_s)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DevOpsScoreHelper
|
||||
module DevOpsReportHelper
|
||||
def score_level(score)
|
||||
if score < 33.33
|
||||
'low'
|
||||
|
|
@ -294,7 +294,8 @@ module IssuablesHelper
|
|||
hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0,
|
||||
issueType: issuable.issue_type,
|
||||
zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable),
|
||||
sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier # rubocop:disable CodeReuse/ActiveRecord
|
||||
sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord
|
||||
iid: issuable.iid.to_s
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -465,6 +466,7 @@ module IssuablesHelper
|
|||
rootPath: root_path,
|
||||
fullPath: issuable[:project_full_path],
|
||||
iid: issuable[:iid],
|
||||
severity: issuable[:severity],
|
||||
timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ module NavHelper
|
|||
end
|
||||
|
||||
def admin_analytics_nav_links
|
||||
%w(dev_ops_score cohorts)
|
||||
%w(dev_ops_report cohorts)
|
||||
end
|
||||
|
||||
def group_issues_sub_menu_items
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module SystemNoteHelper
|
|||
'description' => 'pencil-square',
|
||||
'merge' => 'git-merge',
|
||||
'merged' => 'git-merge',
|
||||
'opened' => 'issue-open',
|
||||
'opened' => 'issues',
|
||||
'closed' => 'issue-close',
|
||||
'time_tracking' => 'timer',
|
||||
'assignee' => 'user',
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@ module Issuable
|
|||
is_a?(TimeTrackable) && !incident?
|
||||
end
|
||||
|
||||
def supports_severity?
|
||||
incident?
|
||||
end
|
||||
|
||||
def incident?
|
||||
is_a?(Issue) && super
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ module LoadedInGroupList
|
|||
.where(members[:source_type].eq(Namespace.name))
|
||||
.where(members[:source_id].eq(namespaces[:id]))
|
||||
.where(members[:requested_at].eq(nil))
|
||||
.where(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -70,7 +71,7 @@ module LoadedInGroupList
|
|||
end
|
||||
|
||||
def member_count
|
||||
@member_count ||= try(:preloaded_member_count) || users.count
|
||||
@member_count ||= try(:preloaded_member_count) || members.count
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DevOpsScore
|
||||
module DevOpsReport
|
||||
class Card
|
||||
attr_accessor :metric, :title, :description, :feature, :blog, :docs
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DevOpsScore
|
||||
module DevOpsReport
|
||||
class IdeaToProductionStep
|
||||
attr_accessor :metric, :title, :features
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DevOpsScore
|
||||
module DevOpsReport
|
||||
class Metric < ApplicationRecord
|
||||
include Presentable
|
||||
|
||||
|
|
@ -20,8 +20,10 @@ class Group < Namespace
|
|||
|
||||
UpdateSharedRunnersError = Class.new(StandardError)
|
||||
|
||||
has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :all_group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :group_members, -> { where(requested_at: nil).where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
|
||||
alias_method :members, :group_members
|
||||
|
||||
has_many :users, through: :group_members
|
||||
has_many :owners,
|
||||
-> { where(members: { access_level: Gitlab::Access::OWNER }) },
|
||||
|
|
@ -395,6 +397,10 @@ class Group < Namespace
|
|||
])
|
||||
end
|
||||
|
||||
def users_count
|
||||
members.count
|
||||
end
|
||||
|
||||
# Returns all users that are members of projects
|
||||
# belonging to the current group or sub-groups
|
||||
def project_users_with_descendants
|
||||
|
|
@ -630,6 +636,7 @@ class Group < Namespace
|
|||
.where(group_member_table[:requested_at].eq(nil))
|
||||
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
|
||||
.where(group_member_table[:source_type].eq('Namespace'))
|
||||
.non_minimal_access
|
||||
end
|
||||
|
||||
def smallest_value_arel(args, column_alias)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ class Member < ApplicationRecord
|
|||
validates :user_id, uniqueness: { scope: [:source_type, :source_id],
|
||||
message: "already exists in source",
|
||||
allow_nil: true }
|
||||
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
|
||||
validate :higher_access_level_than_group, unless: :importing?
|
||||
validates :invite_email,
|
||||
presence: {
|
||||
|
|
@ -60,6 +59,7 @@ class Member < ApplicationRecord
|
|||
left_join_users
|
||||
.where(user_ok)
|
||||
.where(requested_at: nil)
|
||||
.non_minimal_access
|
||||
.reorder(nil)
|
||||
end
|
||||
|
||||
|
|
@ -68,6 +68,8 @@ class Member < ApplicationRecord
|
|||
left_join_users
|
||||
.where(users: { state: 'active' })
|
||||
.non_request
|
||||
.non_invite
|
||||
.non_minimal_access
|
||||
.reorder(nil)
|
||||
end
|
||||
|
||||
|
|
@ -85,6 +87,7 @@ class Member < ApplicationRecord
|
|||
scope :developers, -> { active.where(access_level: DEVELOPER) }
|
||||
scope :maintainers, -> { active.where(access_level: MAINTAINER) }
|
||||
scope :non_guests, -> { where('members.access_level > ?', GUEST) }
|
||||
scope :non_minimal_access, -> { where('members.access_level > ?', MINIMAL_ACCESS) }
|
||||
scope :owners, -> { active.where(access_level: OWNER) }
|
||||
scope :owners_and_maintainers, -> { active.where(access_level: [OWNER, MAINTAINER]) }
|
||||
scope :with_user, -> (user) { where(user: user) }
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ class GroupMember < Member
|
|||
# Make sure group member points only to group as it source
|
||||
default_value_for :source_type, SOURCE_TYPE
|
||||
validates :source_type, format: { with: /\ANamespace\z/ }
|
||||
validates :access_level, presence: true
|
||||
validate :access_level_inclusion
|
||||
|
||||
default_scope { where(source_type: SOURCE_TYPE) } # rubocop:disable Cop/DefaultScope
|
||||
|
||||
scope :of_groups, ->(groups) { where(source_id: groups.select(:id)) }
|
||||
|
|
@ -45,6 +48,12 @@ class GroupMember < Member
|
|||
|
||||
private
|
||||
|
||||
def access_level_inclusion
|
||||
return if access_level.in?(Gitlab::Access.all_values)
|
||||
|
||||
errors.add(:access_level, "is not included in the list")
|
||||
end
|
||||
|
||||
def send_invite
|
||||
run_after_commit_or_now { notification_service.invite_group_member(self, @raw_invite_token) }
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class User < ApplicationRecord
|
|||
|
||||
# Groups
|
||||
has_many :members
|
||||
has_many :group_members, -> { where(requested_at: nil) }, source: 'GroupMember'
|
||||
has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, source: 'GroupMember'
|
||||
has_many :groups, through: :group_members
|
||||
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
|
||||
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DevOpsScore
|
||||
module DevOpsReport
|
||||
class MetricPresenter < Gitlab::View::Presenter::Simple
|
||||
def cards
|
||||
[
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ class IssuableSidebarBasicEntity < Grape::Entity
|
|||
|
||||
expose :supports_time_tracking?, as: :supports_time_tracking
|
||||
expose :supports_milestone?, as: :supports_milestone
|
||||
expose :supports_severity?, as: :supports_severity
|
||||
|
||||
private
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
class IssueSidebarBasicEntity < IssuableSidebarBasicEntity
|
||||
expose :due_date
|
||||
expose :confidential
|
||||
expose :severity
|
||||
end
|
||||
|
||||
IssueSidebarBasicEntity.prepend_if_ee('EE::IssueSidebarBasicEntity')
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ class SubmitUsagePingService
|
|||
end
|
||||
|
||||
def store_metrics(response)
|
||||
metrics = response['conv_index'] || response['dev_ops_score']
|
||||
metrics = response['conv_index'] || response['dev_ops_score'] # leaving dev_ops_score here, as the response data comes from the gitlab-version-com
|
||||
|
||||
return unless metrics.present?
|
||||
|
||||
DevOpsScore::Metric.create!(
|
||||
DevOpsReport::Metric.create!(
|
||||
metrics.slice(*METRICS)
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,37 +17,34 @@
|
|||
.well-segment.well-centered
|
||||
= link_to admin_projects_path do
|
||||
%h3.text-center
|
||||
Projects:
|
||||
= approximate_count_with_delimiters(@counts, Project)
|
||||
= s_('AdminArea|Projects: %{number_of_projects}') % { number_of_projects: approximate_count_with_delimiters(@counts, Project) }
|
||||
%hr
|
||||
= link_to('New project', new_project_path, class: "btn btn-success gl-w-full")
|
||||
= link_to(s_('AdminArea|New project'), new_project_path, class: "btn btn-success gl-w-full")
|
||||
.col-sm-4
|
||||
.info-well.dark-well
|
||||
.well-segment.well-centered
|
||||
= link_to admin_users_path do
|
||||
%h3.text-center
|
||||
Users:
|
||||
= approximate_count_with_delimiters(@counts, User)
|
||||
= s_('AdminArea|Users: %{number_of_users}') % { number_of_users: approximate_count_with_delimiters(@counts, User) }
|
||||
%hr
|
||||
.btn-group.d-flex{ role: 'group' }
|
||||
= link_to 'New user', new_admin_user_path, class: "btn btn-success gl-w-full"
|
||||
= link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn btn-success gl-w-full"
|
||||
= link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn btn-primary gl-w-full'
|
||||
.col-sm-4
|
||||
.info-well.dark-well
|
||||
.well-segment.well-centered
|
||||
= link_to admin_groups_path do
|
||||
%h3.text-center
|
||||
Groups:
|
||||
= approximate_count_with_delimiters(@counts, Group)
|
||||
= s_('AdminArea|Groups: %{number_of_groups}') % { number_of_groups: approximate_count_with_delimiters(@counts, Group) }
|
||||
%hr
|
||||
= link_to 'New group', new_admin_group_path, class: "btn btn-success gl-w-full"
|
||||
= link_to s_('AdminArea|New group'), new_admin_group_path, class: "btn btn-success gl-w-full"
|
||||
.row
|
||||
.col-md-4
|
||||
#js-admin-statistics-container
|
||||
.col-md-4
|
||||
.info-well
|
||||
.well-segment.admin-well.admin-well-features
|
||||
%h4 Features
|
||||
%h4= s_('AdminArea|Features')
|
||||
= feature_entry(_('Sign up'),
|
||||
href: general_admin_application_settings_path(anchor: 'js-signup-settings'),
|
||||
enabled: allow_signup?)
|
||||
|
|
@ -87,42 +84,41 @@
|
|||
.info-well
|
||||
.well-segment.admin-well
|
||||
%h4
|
||||
Components
|
||||
= s_('AdminArea|Components')
|
||||
- if Gitlab::CurrentSettings.version_check_enabled
|
||||
.float-right
|
||||
= version_status_badge
|
||||
%p
|
||||
%a{ href: general_admin_application_settings_path }
|
||||
GitLab
|
||||
= link_to _('GitLab'), general_admin_application_settings_path
|
||||
%span.float-right
|
||||
= Gitlab::VERSION
|
||||
= "(#{Gitlab.revision})"
|
||||
%p
|
||||
GitLab Shell
|
||||
= _('GitLab Shell')
|
||||
%span.float-right
|
||||
= Gitlab::Shell.version
|
||||
%p
|
||||
GitLab Workhorse
|
||||
= _('GitLab Workhorse')
|
||||
%span.float-right
|
||||
= gitlab_workhorse_version
|
||||
%p
|
||||
GitLab API
|
||||
= _('GitLab API')
|
||||
%span.float-right
|
||||
= API::API::version
|
||||
- if Gitlab.config.pages.enabled
|
||||
%p
|
||||
GitLab Pages
|
||||
= _('GitLab Pages')
|
||||
%span.float-right
|
||||
= Gitlab::Pages::VERSION
|
||||
|
||||
= render_if_exists 'admin/dashboard/geo'
|
||||
|
||||
%p
|
||||
Ruby
|
||||
= _('Ruby')
|
||||
%span.float-right
|
||||
#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
|
||||
%p
|
||||
Rails
|
||||
= _('Rails')
|
||||
%span.float-right
|
||||
#{Rails::VERSION::STRING}
|
||||
%p
|
||||
|
|
@ -130,12 +126,12 @@
|
|||
%span.float-right
|
||||
= Gitlab::Database.version
|
||||
%p
|
||||
= link_to "Gitaly Servers", admin_gitaly_servers_path
|
||||
= link_to _("Gitaly Servers"), admin_gitaly_servers_path
|
||||
.row
|
||||
.col-md-4
|
||||
.info-well
|
||||
.well-segment.admin-well
|
||||
%h4 Latest projects
|
||||
%h4= s_('AdminArea|Latest projects')
|
||||
- @projects.each do |project|
|
||||
%p
|
||||
= link_to project.full_name, admin_project_path(project), class: 'str-truncated-60'
|
||||
|
|
@ -144,7 +140,7 @@
|
|||
.col-md-4
|
||||
.info-well
|
||||
.well-segment.admin-well
|
||||
%h4 Latest users
|
||||
%h4= s_('AdminArea|Latest users')
|
||||
- @users.each do |user|
|
||||
%p
|
||||
= link_to [:admin, user], class: 'str-truncated-60' do
|
||||
|
|
@ -154,7 +150,7 @@
|
|||
.col-md-4
|
||||
.info-well
|
||||
.well-segment.admin-well
|
||||
%h4 Latest groups
|
||||
%h4= s_('AdminArea|Latest groups')
|
||||
- @groups.each do |group|
|
||||
%p
|
||||
= link_to [:admin, group], class: 'str-truncated-60' do
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.gl-mt-3
|
||||
.user-callout{ data: { uid: 'dev_ops_score_intro_callout_dismissed' } }
|
||||
.user-callout{ data: { uid: 'dev_ops_report_intro_callout_dismissed' } }
|
||||
.bordered-box.landing.content-block
|
||||
%button.btn.btn-default.close.js-close-callout{ type: 'button',
|
||||
'aria-label' => _('Dismiss DevOps Report introduction') }
|
||||
|
|
@ -10,4 +10,4 @@
|
|||
%p
|
||||
= _('Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers.')
|
||||
.svg-container.devops
|
||||
= custom_icon('dev_ops_score_overview')
|
||||
= custom_icon('dev_ops_report_overview')
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
.container.devops-empty
|
||||
.col-sm-12.justify-content-center.text-center
|
||||
= custom_icon('dev_ops_score_no_data')
|
||||
= custom_icon('dev_ops_report_no_data')
|
||||
%h4= _('Data is still calculating...')
|
||||
%p
|
||||
= _('In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index.')
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
- usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
|
||||
|
||||
.container
|
||||
- if usage_ping_enabled && show_callout?('dev_ops_score_intro_callout_dismissed')
|
||||
- if usage_ping_enabled && show_callout?('dev_ops_report_intro_callout_dismissed')
|
||||
= render 'callout'
|
||||
|
||||
.gl-mt-3
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
%span.gl-ml-5
|
||||
= sprite_icon('users', css_class: 'gl-vertical-align-text-bottom')
|
||||
= number_with_delimiter(group.users.count)
|
||||
= number_with_delimiter(group.users_count)
|
||||
|
||||
%span.gl-ml-5.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
|
||||
= visibility_level_icon(group.visibility_level)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
= _('Gitaly Servers')
|
||||
|
||||
= nav_link(controller: admin_analytics_nav_links) do
|
||||
= link_to admin_dev_ops_score_path, data: { qa_selector: 'admin_analytics_link' } do
|
||||
= link_to admin_dev_ops_report_path, data: { qa_selector: 'admin_analytics_link' } do
|
||||
.nav-icon-container
|
||||
= sprite_icon('chart')
|
||||
%span.nav-item-name
|
||||
|
|
@ -57,12 +57,12 @@
|
|||
|
||||
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_sidebar_analytics_submenu_content' } }
|
||||
= nav_link(controller: admin_analytics_nav_links, html_options: { class: "fly-out-top-item" }) do
|
||||
= link_to admin_dev_ops_score_path do
|
||||
= link_to admin_dev_ops_report_path do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Analytics')
|
||||
%li.divider.fly-out-top-item
|
||||
= nav_link(controller: :dev_ops_score) do
|
||||
= link_to admin_dev_ops_score_path, title: _('DevOps Report') do
|
||||
= nav_link(controller: :dev_ops_report) do
|
||||
= link_to admin_dev_ops_report_path, title: _('DevOps Report') do
|
||||
%span
|
||||
= _('DevOps Report')
|
||||
= nav_link(controller: :cohorts) do
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
#static-site-editor{ data: @config.payload.merge({ merge_requests_illustration_path: image_path('illustrations/merge_requests.svg') }) }
|
||||
#static-site-editor{ data: @data }
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -133,6 +133,9 @@
|
|||
|
||||
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
|
||||
|
||||
- if issuable_sidebar[:supports_severity]
|
||||
#js-severity
|
||||
|
||||
- if issuable_sidebar.dig(:features_available, :health_status)
|
||||
.js-sidebar-status-entry-point
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove virtual scroll list from pipeline test report
|
||||
merge_request: 41935
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Incident severity widget
|
||||
merge_request: 39859
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Incident highlight bar widget
|
||||
merge_request: 41702
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add OmniAuth sign-in via Atlassian Cloud
|
||||
merge_request: 40178
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from admin dashboard
|
||||
merge_request: 41387
|
||||
author: Takuya Noguchi
|
||||
type: other
|
||||
|
|
@ -1404,6 +1404,11 @@ test:
|
|||
app_id: 'YOUR_CLIENT_ID',
|
||||
app_secret: 'YOUR_CLIENT_SECRET'
|
||||
}
|
||||
- { name: 'atlassian_oauth2',
|
||||
app_id: 'YOUR_CLIENT_ID',
|
||||
app_secret: 'YOUR_CLIENT_SECRET',
|
||||
args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
|
||||
}
|
||||
ldap:
|
||||
enabled: false
|
||||
servers:
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ Rails.application.routes.draw do
|
|||
# Use this scope for all new global routes.
|
||||
scope path: '-' do
|
||||
# remove in 13.5
|
||||
get '/instance_statistics', to: redirect('admin/dev_ops_score')
|
||||
get '/instance_statistics/dev_ops_score', to: redirect('admin/dev_ops_score')
|
||||
get '/instance_statistics', to: redirect('admin/dev_ops_report')
|
||||
get '/instance_statistics/dev_ops_score', to: redirect('admin/dev_ops_report')
|
||||
get '/instance_statistics/cohorts', to: redirect('admin/cohorts')
|
||||
# Autocomplete
|
||||
get '/autocomplete/users' => 'autocomplete#users'
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@ namespace :admin do
|
|||
resources :projects, only: [:index]
|
||||
|
||||
resources :instance_statistics, only: :index
|
||||
resource :dev_ops_score, controller: 'dev_ops_score', only: :show
|
||||
resource :dev_ops_report, controller: 'dev_ops_report', only: :show
|
||||
# remove in 13.5
|
||||
get '/dev_ops_score', to: redirect('admin/dev_ops_report')
|
||||
resources :cohorts, only: :index
|
||||
|
||||
scope(path: 'projects/*namespace_id',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Gitlab::Seeder.quiet do
|
||||
dev_ops_score_metric = DevOpsScore::Metric.new(
|
||||
dev_ops_report_metric = DevOpsReport::Metric.new(
|
||||
leader_issues: 10.2,
|
||||
instance_issues: 3.2,
|
||||
|
||||
|
|
@ -31,10 +31,10 @@ Gitlab::Seeder.quiet do
|
|||
instance_service_desk_issues: 15.1
|
||||
)
|
||||
|
||||
if dev_ops_score_metric.save
|
||||
if dev_ops_report_metric.save
|
||||
print '.'
|
||||
else
|
||||
puts dev_ops_score_metric.errors.full_messages
|
||||
puts dev_ops_report_metric.errors.full_messages
|
||||
print 'F'
|
||||
end
|
||||
end
|
||||
|
|
@ -11,6 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
GitLab integrates with the following external authentication and authorization
|
||||
providers:
|
||||
|
||||
- [Atlassian](atlassian.md)
|
||||
- [Auth0](../../integration/auth0.md)
|
||||
- [Authentiq](authentiq.md)
|
||||
- [AWS Cognito](cognito.md)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
type: reference
|
||||
stage: Manage
|
||||
group: Access
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Atlassian OmniAuth Provider
|
||||
|
||||
To enable the Atlassian OmniAuth provider for passwordless authentication you must register an application with Atlassian.
|
||||
|
||||
## Atlassian application registration
|
||||
|
||||
1. Go to <https://developer.atlassian.com/apps/> and sign-in with the Atlassian
|
||||
account that will administer the application.
|
||||
|
||||
1. Click **Create a new app**.
|
||||
|
||||
1. Choose an App Name, such as 'GitLab', and click **Create**.
|
||||
|
||||
1. Note the `Client ID` and `Secret` for the [GitLab configuration](#gitlab-configuration) steps.
|
||||
|
||||
1. In the left sidebar under **APIS AND FEATURES**, click **OAuth 2.0 (3LO)**.
|
||||
|
||||
1. Enter the GitLab callback URL using the format `https://gitlab.example.com/users/auth/atlassian_oauth2/callback` and click **Save changes**.
|
||||
|
||||
1. Click **+ Add** in the left sidebar under **APIS AND FEATURES**.
|
||||
|
||||
1. Click **Add** for **Jira platform REST API** and then **Configure**.
|
||||
|
||||
1. Click **Add** next to the following scopes:
|
||||
- **View Jira issue data**
|
||||
- **View user profiles**
|
||||
- **Create and manage issues**
|
||||
|
||||
## GitLab configuration
|
||||
|
||||
1. On your GitLab server, open the configuration file:
|
||||
|
||||
For Omnibus GitLab installations:
|
||||
|
||||
```shell
|
||||
sudo editor /etc/gitlab/gitlab.rb
|
||||
```
|
||||
|
||||
For installations from source:
|
||||
|
||||
```shell
|
||||
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
|
||||
```
|
||||
|
||||
1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add `atlassian_oauth2` as an OAuth provider.
|
||||
|
||||
1. Add the provider configuration for Atlassian:
|
||||
|
||||
For Omnibus GitLab installations:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['omniauth_providers'] = [
|
||||
{
|
||||
name: "atlassian_oauth2",
|
||||
app_id: "YOUR_CLIENT_ID",
|
||||
app_secret: "YOUR_CLIENT_SECRET",
|
||||
args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
For installations from source:
|
||||
|
||||
```yaml
|
||||
- name: "atlassian_oauth2",
|
||||
app_id: "YOUR_CLIENT_ID",
|
||||
app_secret: "YOUR_CLIENT_SECRET",
|
||||
args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
|
||||
```
|
||||
|
||||
1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in [application registration](#atlassian-application-registration) steps.
|
||||
|
||||
1. Save the configuration file.
|
||||
|
||||
1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
|
||||
|
||||
On the sign-in page there should now be an Atlassian icon below the regular sign in form. Click the icon to begin the authentication process.
|
||||
|
||||
If everything goes right, the user is signed in to GitLab using their Atlassian credentials.
|
||||
|
|
@ -750,11 +750,15 @@ U = <user_id>
|
|||
# Get required details / objects
|
||||
user = User.find_by_id(U)
|
||||
project = Project.find_by_id(P)
|
||||
repo = ContainerRepository.find_by(project_id: P)
|
||||
policy = ContainerExpirationPolicy.find_by(project_id: P)
|
||||
|
||||
# Start the tag cleanup
|
||||
Projects::ContainerRepository::CleanupTagsService.new(project, user, policy.attributes.except("created_at", "updated_at")).execute(repo)
|
||||
# Loop through each container repository
|
||||
project.container_repositories.find_each do |repo|
|
||||
puts repo.attributes
|
||||
|
||||
# Start the tag cleanup
|
||||
puts Projects::ContainerRepository::CleanupTagsService.new(project, user, policy.attributes.except("created_at", "updated_at")).execute(repo)
|
||||
end
|
||||
```
|
||||
|
||||
NOTE: **Note:**
|
||||
|
|
|
|||
|
|
@ -13088,6 +13088,41 @@ type Project {
|
|||
state: [VulnerabilityState!]
|
||||
): VulnerabilityConnection
|
||||
|
||||
"""
|
||||
Number of vulnerabilities per day for the project
|
||||
"""
|
||||
vulnerabilitiesCountByDay(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
Last day for which to fetch vulnerability history
|
||||
"""
|
||||
endDate: ISO8601Date!
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
|
||||
"""
|
||||
First day for which to fetch vulnerability history
|
||||
"""
|
||||
startDate: ISO8601Date!
|
||||
): VulnerabilitiesCountByDayConnection
|
||||
|
||||
"""
|
||||
Vulnerability scanners reported on the project vulnerabilties
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -38171,6 +38171,87 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilitiesCountByDay",
|
||||
"description": "Number of vulnerabilities per day for the project",
|
||||
"args": [
|
||||
{
|
||||
"name": "startDate",
|
||||
"description": "First day for which to fetch vulnerability history",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ISO8601Date",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"description": "Last day for which to fetch vulnerability history",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ISO8601Date",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "VulnerabilitiesCountByDayConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "vulnerabilityScanners",
|
||||
"description": "Vulnerability scanners reported on the project vulnerabilties",
|
||||
|
|
|
|||
|
|
@ -3,12 +3,7 @@ stage: Verify
|
|||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
|
||||
author: Fabio Busatto
|
||||
author_gitlab: bikebilly
|
||||
level: intermediate
|
||||
article_type: tutorial
|
||||
type: tutorial
|
||||
date: 2017-08-15
|
||||
---
|
||||
|
||||
# How to deploy Maven projects to Artifactory with GitLab CI/CD
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
stage: Release
|
||||
group: Progressive Delivery
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
author: Dylan Griffith
|
||||
author_gitlab: DylanGriffith
|
||||
level: intermediate
|
||||
article_type: tutorial
|
||||
type: tutorial
|
||||
date: 2018-06-07
|
||||
last_updated: 2019-04-08
|
||||
description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD"
|
||||
---
|
||||
|
||||
# Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@
|
|||
stage: Verify
|
||||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
author: Ryan Hall
|
||||
author_gitlab: blitzgren
|
||||
level: intermediate
|
||||
article_type: tutorial
|
||||
type: tutorial
|
||||
date: 2018-03-07
|
||||
last_updated: 2019-03-11
|
||||
---
|
||||
|
||||
# DevOps and Game Dev with GitLab CI/CD
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@
|
|||
stage: Verify
|
||||
group: Testing
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
author: Vincent Tunru
|
||||
author_gitlab: Vinnl
|
||||
level: advanced
|
||||
article_type: user guide
|
||||
type: tutorial
|
||||
date: 2019-02-18
|
||||
description: 'Confidence checking your entire app every time a new feature is added can quickly become repetitive. Learn how to automate it with GitLab CI/CD.'
|
||||
---
|
||||
|
||||
# End-to-end testing with GitLab CI/CD and WebdriverIO
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
stage: Verify
|
||||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
|
||||
author: Mehran Rasulian
|
||||
author_gitlab: mehranrasulian
|
||||
level: intermediate
|
||||
article_type: tutorial
|
||||
type: tutorial
|
||||
date: 2017-08-31
|
||||
last_updated: 2019-03-06
|
||||
---
|
||||
|
||||
# Test and deploy Laravel applications with GitLab CI/CD and Envoy
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@
|
|||
stage: Verify
|
||||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
author: Alexandre S Hostert
|
||||
author_gitlab: Hostert
|
||||
level: beginner
|
||||
article_type: tutorial
|
||||
type: tutorial
|
||||
date: 2018-02-20
|
||||
last_updated: 2019-03-06
|
||||
---
|
||||
|
||||
# Testing a Phoenix application with GitLab CI/CD
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
stage: Verify
|
||||
group: Continuous Integration
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
last_updated: 2017-12-13
|
||||
type: tutorial
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
---
|
||||
last_updated: 2020-09-01
|
||||
---
|
||||
|
||||
# Managing PostgreSQL extensions
|
||||
|
||||
This guide documents how to manage PostgreSQL extensions for installations with an external
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
---
|
||||
type: tutorial, concepts
|
||||
description: "How to migrate an existing Git repository to Git LFS with BFG."
|
||||
last_updated: 2019-07-11
|
||||
---
|
||||
|
||||
# Migrate a Git repo into Git LFS with BFG
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
---
|
||||
last_updated: 2019-06-18
|
||||
---
|
||||
|
||||
# Migrating from MySQL to PostgreSQL
|
||||
|
||||
This guide documents how to take a working GitLab instance that uses MySQL and
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ module Gitlab
|
|||
module Access
|
||||
AccessDeniedError = Class.new(StandardError)
|
||||
|
||||
NO_ACCESS = 0
|
||||
GUEST = 10
|
||||
REPORTER = 20
|
||||
DEVELOPER = 30
|
||||
MAINTAINER = 40
|
||||
OWNER = 50
|
||||
NO_ACCESS = 0
|
||||
MINIMAL_ACCESS = 5
|
||||
GUEST = 10
|
||||
REPORTER = 20
|
||||
DEVELOPER = 30
|
||||
MAINTAINER = 40
|
||||
OWNER = 50
|
||||
|
||||
# Branch protection settings
|
||||
PROTECTION_NONE = 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Auth
|
||||
module Atlassian
|
||||
class AuthHash < Gitlab::Auth::OAuth::AuthHash
|
||||
def token
|
||||
credentials[:token]
|
||||
end
|
||||
|
||||
def refresh_token
|
||||
credentials[:refresh_token]
|
||||
end
|
||||
|
||||
def expires?
|
||||
credentials[:expires]
|
||||
end
|
||||
|
||||
def expires_at
|
||||
credentials[:expires_at]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def credentials
|
||||
auth_hash[:credentials]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Auth
|
||||
module Atlassian
|
||||
class IdentityLinker < OmniauthIdentityLinkerBase
|
||||
extend ::Gitlab::Utils::Override
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
private
|
||||
|
||||
override :identity
|
||||
def identity
|
||||
strong_memoize(:identity) do
|
||||
current_user.atlassian_identity || build_atlassian_identity
|
||||
end
|
||||
end
|
||||
|
||||
def build_atlassian_identity
|
||||
identity = current_user.build_atlassian_identity
|
||||
::Gitlab::Auth::Atlassian::User.assign_identity_from_auth_hash!(identity, auth_hash)
|
||||
end
|
||||
|
||||
def auth_hash
|
||||
::Gitlab::Auth::Atlassian::AuthHash.new(oauth)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Auth
|
||||
module Atlassian
|
||||
class User < Gitlab::Auth::OAuth::User
|
||||
def self.assign_identity_from_auth_hash!(identity, auth_hash)
|
||||
identity.extern_uid = auth_hash.uid
|
||||
identity.token = auth_hash.token
|
||||
identity.refresh_token = auth_hash.refresh_token
|
||||
identity.expires_at = Time.at(auth_hash.expires_at).utc.to_datetime if auth_hash.expires?
|
||||
|
||||
identity
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_by_uid_and_provider
|
||||
::Atlassian::Identity.find_by_extern_uid(auth_hash.uid)&.user
|
||||
end
|
||||
|
||||
def add_or_update_user_identities
|
||||
return unless gl_user
|
||||
|
||||
identity = gl_user.atlassian_identity || gl_user.build_atlassian_identity
|
||||
self.class.assign_identity_from_auth_hash!(identity, auth_hash)
|
||||
end
|
||||
|
||||
def auth_hash=(auth_hash)
|
||||
@auth_hash = ::Gitlab::Auth::Atlassian::AuthHash.new(auth_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,10 +5,11 @@ module Gitlab
|
|||
module OAuth
|
||||
class Provider
|
||||
LABELS = {
|
||||
"github" => "GitHub",
|
||||
"gitlab" => "GitLab.com",
|
||||
"google_oauth2" => "Google",
|
||||
"azure_oauth2" => "Azure AD"
|
||||
"github" => "GitHub",
|
||||
"gitlab" => "GitLab.com",
|
||||
"google_oauth2" => "Google",
|
||||
"azure_oauth2" => "Azure AD",
|
||||
'atlassian_oauth2' => 'Atlassian'
|
||||
}.freeze
|
||||
|
||||
def self.authentication(user, provider)
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ module Gitlab
|
|||
|
||||
def path
|
||||
if ::Gitlab::Runtime.web_server? && ENV['GITLAB_PAGES_DENY_DISK_ACCESS'] == '1'
|
||||
begin
|
||||
raise DiskAccessDenied
|
||||
rescue DiskAccessDenied => ex
|
||||
::Gitlab::ErrorTracking.track_exception(ex)
|
||||
end
|
||||
raise DiskAccessDenied
|
||||
end
|
||||
|
||||
super
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ module Gitlab
|
|||
.and(members[:source_type].eq('Namespace'))
|
||||
.and(members[:requested_at].eq(nil))
|
||||
.and(members[:user_id].eq(user.id))
|
||||
.and(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
|
||||
|
||||
Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
|
||||
end
|
||||
|
|
@ -119,6 +120,7 @@ module Gitlab
|
|||
.and(members[:source_type].eq('Namespace'))
|
||||
.and(members[:requested_at].eq(nil))
|
||||
.and(members[:user_id].eq(user.id))
|
||||
.and(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
|
||||
Arel::Nodes::InnerJoin.new(members, Arel::Nodes::On.new(cond))
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module StaticSiteEditor
|
||||
class Config
|
||||
SUPPORTED_EXTENSIONS = %w[.md].freeze
|
||||
|
||||
def initialize(repository, ref, file_path, return_url)
|
||||
@repository = repository
|
||||
@ref = ref
|
||||
@file_path = file_path
|
||||
@return_url = return_url
|
||||
@commit_id = repository.commit(ref)&.id if ref
|
||||
end
|
||||
|
||||
def payload
|
||||
{
|
||||
branch: ref,
|
||||
path: file_path,
|
||||
commit_id: commit_id,
|
||||
project_id: project.id,
|
||||
project: project.path,
|
||||
namespace: project.namespace.full_path,
|
||||
return_url: sanitize_url(return_url),
|
||||
is_supported_content: supported_content?.to_s,
|
||||
base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :repository, :ref, :file_path, :return_url, :commit_id
|
||||
|
||||
delegate :project, to: :repository
|
||||
|
||||
def supported_content?
|
||||
master_branch? && extension_supported? && file_exists?
|
||||
end
|
||||
|
||||
def master_branch?
|
||||
ref == 'master'
|
||||
end
|
||||
|
||||
def extension_supported?
|
||||
return true if file_path.end_with?('.md.erb') && Feature.enabled?(:sse_erb_support, project)
|
||||
|
||||
SUPPORTED_EXTENSIONS.any? { |ext| file_path.end_with?(ext) }
|
||||
end
|
||||
|
||||
def file_exists?
|
||||
commit_id.present? && !repository.blob_at(commit_id, file_path).nil?
|
||||
end
|
||||
|
||||
def full_path
|
||||
"#{ref}/#{file_path}"
|
||||
end
|
||||
|
||||
def sanitize_url(url)
|
||||
url if Gitlab::UrlSanitizer.valid_web?(url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module StaticSiteEditor
|
||||
module Config
|
||||
class CombinedConfig
|
||||
def initialize(repository, ref, path, return_url)
|
||||
@repository = repository
|
||||
@ref = ref
|
||||
@path = path
|
||||
@return_url = return_url
|
||||
end
|
||||
|
||||
def data
|
||||
generated_data = Gitlab::StaticSiteEditor::Config::GeneratedConfig.new(
|
||||
@repository,
|
||||
@ref,
|
||||
@path,
|
||||
@return_url
|
||||
).data
|
||||
file_data = Gitlab::StaticSiteEditor::Config::FileConfig.new.data
|
||||
check_for_duplicate_keys(generated_data, file_data)
|
||||
generated_data.merge(file_data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_for_duplicate_keys(generated_data, file_data)
|
||||
duplicate_keys = generated_data.keys & file_data.keys
|
||||
raise StandardError.new("Duplicate key(s) '#{duplicate_keys}' found.") if duplicate_keys.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module StaticSiteEditor
|
||||
module Config
|
||||
class FileConfig
|
||||
def data
|
||||
merge_requests_illustration_path = ActionController::Base.helpers.image_path('illustrations/merge_requests.svg')
|
||||
{
|
||||
merge_requests_illustration_path: merge_requests_illustration_path
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module StaticSiteEditor
|
||||
module Config
|
||||
class GeneratedConfig
|
||||
SUPPORTED_EXTENSIONS = %w[.md].freeze
|
||||
|
||||
def initialize(repository, ref, path, return_url)
|
||||
@repository = repository
|
||||
@ref = ref
|
||||
@path = path
|
||||
@return_url = return_url
|
||||
end
|
||||
|
||||
def data
|
||||
{
|
||||
branch: ref,
|
||||
path: path,
|
||||
commit_id: commit_id,
|
||||
project_id: project.id,
|
||||
project: project.path,
|
||||
namespace: project.namespace.full_path,
|
||||
return_url: sanitize_url(return_url),
|
||||
is_supported_content: supported_content?.to_s,
|
||||
base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :repository, :ref, :path, :return_url
|
||||
|
||||
delegate :project, to: :repository
|
||||
|
||||
def commit_id
|
||||
repository.commit(ref)&.id if ref
|
||||
end
|
||||
|
||||
def supported_content?
|
||||
master_branch? && extension_supported? && file_exists?
|
||||
end
|
||||
|
||||
def master_branch?
|
||||
ref == 'master'
|
||||
end
|
||||
|
||||
def extension_supported?
|
||||
return true if path.end_with?('.md.erb') && Feature.enabled?(:sse_erb_support, project)
|
||||
|
||||
SUPPORTED_EXTENSIONS.any? { |ext| path.end_with?(ext) }
|
||||
end
|
||||
|
||||
def file_exists?
|
||||
commit_id.present? && !repository.blob_at(commit_id, path).nil?
|
||||
end
|
||||
|
||||
def full_path
|
||||
"#{ref}/#{path}"
|
||||
end
|
||||
|
||||
def sanitize_url(url)
|
||||
url if Gitlab::UrlSanitizer.valid_web?(url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1776,21 +1776,51 @@ msgstr ""
|
|||
msgid "AdminArea|Bots"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Components"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Developer"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Groups: %{number_of_groups}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Guest"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Included Free in license"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Latest groups"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Latest projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Latest users"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Maintainer"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|New group"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|New project"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|New user"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Owner"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Projects: %{number_of_projects}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Reporter"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1818,6 +1848,9 @@ msgstr ""
|
|||
msgid "AdminArea|Users without a Group and Project"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|Users: %{number_of_users}"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -10772,7 +10805,7 @@ msgstr ""
|
|||
msgid "FeatureFlags|Include additional user IDs"
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|Install a %{docs_link_anchored_start}compatible client library%{docs_link_anchored_end} and specify the API URL, application name, and instance ID during the configuration setup. %{docs_link_start}More Information%{docs_link_end}"
|
||||
msgid "FeatureFlags|Install a %{docsLinkAnchoredStart}compatible client library%{docsLinkAnchoredEnd} and specify the API URL, application name, and instance ID during the configuration setup. %{docsLinkStart}More Information%{docsLinkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|Instance ID"
|
||||
|
|
@ -11696,9 +11729,15 @@ msgstr ""
|
|||
msgid "GitHub import"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab / Unsubscribe"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab API"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Enterprise Edition %{plan}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11711,12 +11750,18 @@ msgstr ""
|
|||
msgid "GitLab Issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Pages"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Service Desk is a simple way to allow people to create issues in your GitLab instance without needing their own user account. It provides a unique email address for end users to create issues in a project, and replies can be sent either through the GitLab interface or by email. End users will only see the thread through email."
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Shell"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Support Bot"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11726,6 +11771,9 @@ msgstr ""
|
|||
msgid "GitLab User"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab Workhorse"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -12807,6 +12855,15 @@ msgstr ""
|
|||
msgid "Highest role:"
|
||||
msgstr ""
|
||||
|
||||
msgid "HighlightBar|Alert events:"
|
||||
msgstr ""
|
||||
|
||||
msgid "HighlightBar|Alert start time:"
|
||||
msgstr ""
|
||||
|
||||
msgid "HighlightBar|Original alert:"
|
||||
msgstr ""
|
||||
|
||||
msgid "History"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -13252,18 +13309,30 @@ msgstr ""
|
|||
msgid "IncidentManagement|Create incident"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Critical - S1"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Date created"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Display your incidents in a dedicated view"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|High - S2"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Incident"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Incidents"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Low - S4"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Medium - S3"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|No incidents to display."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -13285,6 +13354,9 @@ msgstr ""
|
|||
msgid "IncidentManagement|Unassigned"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Unknown"
|
||||
msgstr ""
|
||||
|
||||
msgid "IncidentManagement|Unpublished"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -20487,6 +20559,9 @@ msgstr ""
|
|||
msgid "README"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rails"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rake Tasks Help"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -21513,6 +21588,9 @@ msgstr ""
|
|||
msgid "Rook"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ruby"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rule name is already taken."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -22934,6 +23012,15 @@ msgstr ""
|
|||
msgid "Severity"
|
||||
msgstr ""
|
||||
|
||||
msgid "SeverityWidget|Severity"
|
||||
msgstr ""
|
||||
|
||||
msgid "SeverityWidget|Severity: %{severity}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SeverityWidget|There was an error while updating severity."
|
||||
msgstr ""
|
||||
|
||||
msgid "Shards (%{shards})"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943' do
|
||||
it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247281', type: :investigating } do
|
||||
uri = URI.parse(Runtime::Scenario.gitlab_address)
|
||||
gitlab_address_with_port = "#{uri.scheme}://#{uri.host}:#{uri.port}"
|
||||
pom_xml = {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'publishes an npm package and then deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/944' do
|
||||
it 'publishes an npm package and then deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/944', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247281', type: :investigating } do
|
||||
uri = URI.parse(Runtime::Scenario.gitlab_address)
|
||||
gitlab_host_with_port = "#{uri.host}:#{uri.port}"
|
||||
gitlab_address_with_port = "#{uri.scheme}://#{uri.host}:#{uri.port}"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
it 'works with Auto DevOps' do
|
||||
it 'works with Auto DevOps', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/240946', type: :flaky } do
|
||||
%w[build code_quality test].each do |job|
|
||||
pipeline.visit!
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478' do
|
||||
it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247467', type: :investigating } do
|
||||
# Issue pages always make AJAX requests
|
||||
Resource::Issue.fabricate_via_browser_ui! do |issue|
|
||||
issue.title = 'Performance bar test'
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ module RuboCop
|
|||
#
|
||||
# @example
|
||||
# # bad
|
||||
# root to: redirect('/-/instance/statistics/dev_ops_score')
|
||||
# root to: redirect('/-/instance_statistics/dev_ops_report')
|
||||
#
|
||||
# # good
|
||||
# root to: redirect('-/instance/statistics/dev_ops_score')
|
||||
# root to: redirect('-/instance_statistics/dev_ops_report')
|
||||
#
|
||||
|
||||
class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Cop
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Admin::DevOpsScoreController do
|
||||
RSpec.describe Admin::DevOpsReportController do
|
||||
describe 'GET #show' do
|
||||
context 'as admin' do
|
||||
let(:user) { create(:admin) }
|
||||
|
|
@ -276,6 +276,51 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context 'atlassian_oauth2' do
|
||||
let(:provider) { :atlassian_oauth2 }
|
||||
let(:extern_uid) { 'my-uid' }
|
||||
|
||||
context 'when the user and identity already exist' do
|
||||
let(:user) { create(:atlassian_user, extern_uid: extern_uid) }
|
||||
|
||||
it 'allows sign-in' do
|
||||
post :atlassian_oauth2
|
||||
|
||||
expect(request.env['warden']).to be_authenticated
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a new user' do
|
||||
before do
|
||||
stub_omniauth_setting(enabled: true, auto_link_user: true, allow_single_sign_on: ['atlassian_oauth2'])
|
||||
|
||||
user.destroy
|
||||
end
|
||||
|
||||
it 'denies sign-in if sign-up is enabled, but block_auto_created_users is set' do
|
||||
post :atlassian_oauth2
|
||||
|
||||
expect(flash[:alert]).to start_with 'Your account has been blocked.'
|
||||
end
|
||||
|
||||
it 'accepts sign-in if sign-up is enabled' do
|
||||
stub_omniauth_setting(block_auto_created_users: false)
|
||||
|
||||
post :atlassian_oauth2
|
||||
|
||||
expect(request.env['warden']).to be_authenticated
|
||||
end
|
||||
|
||||
it 'denies sign-in if sign-up is not enabled' do
|
||||
stub_omniauth_setting(allow_single_sign_on: false, block_auto_created_users: false)
|
||||
|
||||
post :atlassian_oauth2
|
||||
|
||||
expect(flash[:alert]).to start_with 'Signing in using your Atlassian account without a pre-existing GitLab account is not allowed.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'salesforce' do
|
||||
let(:extern_uid) { 'my-uid' }
|
||||
let(:provider) { :salesforce }
|
||||
|
|
|
|||