Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6535cf9c79
commit
c663374b3d
71 changed files with 481 additions and 234 deletions
|
@ -43,11 +43,8 @@ Graphql/ResolverType:
|
|||
Exclude:
|
||||
- 'app/graphql/resolvers/base_resolver.rb'
|
||||
- 'app/graphql/resolvers/ci/pipeline_stages_resolver.rb'
|
||||
- 'app/graphql/resolvers/commit_pipelines_resolver.rb'
|
||||
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
|
||||
- 'app/graphql/resolvers/merge_request_pipelines_resolver.rb'
|
||||
- 'app/graphql/resolvers/merge_requests_resolver.rb'
|
||||
- 'app/graphql/resolvers/project_pipelines_resolver.rb'
|
||||
- 'app/graphql/resolvers/users/group_count_resolver.rb'
|
||||
- 'ee/app/graphql/resolvers/ci/jobs_resolver.rb'
|
||||
- 'ee/app/graphql/resolvers/geo/merge_request_diff_registries_resolver.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
b8a027e4ea96235545000da5ff35d05a91b4370b
|
||||
f869e2716122b17ce78508aacf981a098406d2d7
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { GlLoadingIcon, GlButton } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlButton, GlAlert, GlSafeHtmlDirective } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlButton,
|
||||
GlAlert,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml: GlSafeHtmlDirective,
|
||||
},
|
||||
props: {
|
||||
isLoading: {
|
||||
|
@ -59,7 +62,9 @@ export default {
|
|||
{{ __('Start Web Terminal') }}
|
||||
</gl-button>
|
||||
</p>
|
||||
<div v-if="!isValid && message" class="bs-callout gl-text-left" v-html="message"></div>
|
||||
<gl-alert v-if="!isValid && message" variant="tip" :dismissible="false">
|
||||
<span v-safe-html="message"></span>
|
||||
</gl-alert>
|
||||
<p v-else>
|
||||
<a
|
||||
v-if="helpPath"
|
||||
|
|
|
@ -29,6 +29,7 @@ export default {
|
|||
},
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.handleWindowResize);
|
||||
this.updatePageContainerClass();
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleWindowResize);
|
||||
|
|
|
@ -5,12 +5,12 @@ import ModalManager from './components/user_modal_manager.vue';
|
|||
import DeleteUserModal from './components/delete_user_modal.vue';
|
||||
import UserOperationConfirmationModal from './components/user_operation_confirmation_modal.vue';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import initConfirmModal from '~/confirm_modal';
|
||||
|
||||
const MODAL_TEXTS_CONTAINER_SELECTOR = '#modal-texts';
|
||||
const MODAL_MANAGER_SELECTOR = '#user-modal';
|
||||
const ACTION_MODALS = {
|
||||
deactivate: UserOperationConfirmationModal,
|
||||
block: UserOperationConfirmationModal,
|
||||
delete: DeleteUserModal,
|
||||
'delete-with-contributions': DeleteUserModal,
|
||||
};
|
||||
|
@ -62,4 +62,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
});
|
||||
},
|
||||
});
|
||||
|
||||
initConfirmModal();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const DOWNSTREAM = 'downstream';
|
||||
export const MAIN = 'main';
|
||||
export const UPSTREAM = 'upstream';
|
|
@ -5,6 +5,7 @@ import StageColumnComponent from './stage_column_component.vue';
|
|||
import GraphWidthMixin from '../../mixins/graph_width_mixin';
|
||||
import LinkedPipelinesColumn from './linked_pipelines_column.vue';
|
||||
import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
|
||||
import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
|
||||
|
||||
export default {
|
||||
name: 'PipelineGraph',
|
||||
|
@ -35,11 +36,11 @@ export default {
|
|||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'main',
|
||||
default: MAIN,
|
||||
},
|
||||
},
|
||||
upstream: 'upstream',
|
||||
downstream: 'downstream',
|
||||
upstream: UPSTREAM,
|
||||
downstream: DOWNSTREAM,
|
||||
data() {
|
||||
return {
|
||||
downstreamMarginTop: null,
|
||||
|
@ -54,41 +55,41 @@ export default {
|
|||
graph() {
|
||||
return this.pipeline.details?.stages;
|
||||
},
|
||||
hasTriggeredBy() {
|
||||
hasUpstream() {
|
||||
return (
|
||||
this.type !== this.$options.downstream &&
|
||||
this.triggeredByPipelines &&
|
||||
this.upstreamPipelines &&
|
||||
this.pipeline.triggered_by !== null
|
||||
);
|
||||
},
|
||||
triggeredByPipelines() {
|
||||
upstreamPipelines() {
|
||||
return this.pipeline.triggered_by;
|
||||
},
|
||||
hasTriggered() {
|
||||
hasDownstream() {
|
||||
return (
|
||||
this.type !== this.$options.upstream &&
|
||||
this.triggeredPipelines &&
|
||||
this.downstreamPipelines &&
|
||||
this.pipeline.triggered.length > 0
|
||||
);
|
||||
},
|
||||
triggeredPipelines() {
|
||||
downstreamPipelines() {
|
||||
return this.pipeline.triggered;
|
||||
},
|
||||
expandedTriggeredBy() {
|
||||
expandedUpstream() {
|
||||
return (
|
||||
this.pipeline.triggered_by &&
|
||||
Array.isArray(this.pipeline.triggered_by) &&
|
||||
this.pipeline.triggered_by.find(el => el.isExpanded)
|
||||
);
|
||||
},
|
||||
expandedTriggered() {
|
||||
expandedDownstream() {
|
||||
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
|
||||
},
|
||||
pipelineTypeUpstream() {
|
||||
return this.type !== this.$options.downstream && this.expandedTriggeredBy;
|
||||
return this.type !== this.$options.downstream && this.expandedUpstream;
|
||||
},
|
||||
pipelineTypeDownstream() {
|
||||
return this.type !== this.$options.upstream && this.expandedTriggered;
|
||||
return this.type !== this.$options.upstream && this.expandedDownstream;
|
||||
},
|
||||
pipelineProjectId() {
|
||||
return this.pipeline.project.id;
|
||||
|
@ -142,11 +143,11 @@ export default {
|
|||
* and we want to reset the pipeline store. Triggering the reset without
|
||||
* this condition would mean not allowing downstreams of downstreams to expand
|
||||
*/
|
||||
if (this.expandedTriggered?.id !== pipeline.id) {
|
||||
this.$emit('onResetTriggered', this.pipeline, pipeline);
|
||||
if (this.expandedDownstream?.id !== pipeline.id) {
|
||||
this.$emit('onResetDownstream', this.pipeline, pipeline);
|
||||
}
|
||||
|
||||
this.$emit('onClickTriggered', pipeline);
|
||||
this.$emit('onClickDownstreamPipeline', pipeline);
|
||||
},
|
||||
calculateMarginTop(downstreamNode, pixelDiff) {
|
||||
return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
|
||||
|
@ -154,8 +155,8 @@ export default {
|
|||
hasOnlyOneJob(stage) {
|
||||
return stage.groups.length === 1;
|
||||
},
|
||||
hasUpstream(index) {
|
||||
return index === 0 && this.hasTriggeredBy;
|
||||
hasUpstreamColumn(index) {
|
||||
return index === 0 && this.hasUpstream;
|
||||
},
|
||||
setJob(jobName) {
|
||||
this.jobName = jobName;
|
||||
|
@ -192,30 +193,30 @@ export default {
|
|||
|
||||
<pipeline-graph
|
||||
v-if="pipelineTypeUpstream"
|
||||
type="upstream"
|
||||
:type="$options.upstream"
|
||||
class="d-inline-block upstream-pipeline"
|
||||
:class="`js-upstream-pipeline-${expandedTriggeredBy.id}`"
|
||||
:class="`js-upstream-pipeline-${expandedUpstream.id}`"
|
||||
:is-loading="false"
|
||||
:pipeline="expandedTriggeredBy"
|
||||
:pipeline="expandedUpstream"
|
||||
:is-linked-pipeline="true"
|
||||
:mediator="mediator"
|
||||
@onClickTriggeredBy="clickTriggeredByPipeline"
|
||||
@onClickUpstreamPipeline="clickUpstreamPipeline"
|
||||
@refreshPipelineGraph="requestRefreshPipelineGraph"
|
||||
/>
|
||||
|
||||
<linked-pipelines-column
|
||||
v-if="hasTriggeredBy"
|
||||
:linked-pipelines="triggeredByPipelines"
|
||||
v-if="hasUpstream"
|
||||
:type="$options.upstream"
|
||||
:linked-pipelines="upstreamPipelines"
|
||||
:column-title="__('Upstream')"
|
||||
:project-id="pipelineProjectId"
|
||||
graph-position="left"
|
||||
@linkedPipelineClick="$emit('onClickTriggeredBy', $event)"
|
||||
@linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
|
||||
/>
|
||||
|
||||
<ul
|
||||
v-if="!isLoading"
|
||||
:class="{
|
||||
'inline js-has-linked-pipelines': hasTriggered || hasTriggeredBy,
|
||||
'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
|
||||
}"
|
||||
class="stage-column-list align-top"
|
||||
>
|
||||
|
@ -223,7 +224,7 @@ export default {
|
|||
v-for="(stage, index) in graph"
|
||||
:key="stage.name"
|
||||
:class="{
|
||||
'has-upstream gl-ml-11': hasUpstream(index),
|
||||
'has-upstream gl-ml-11': hasUpstreamColumn(index),
|
||||
'has-only-one-job': hasOnlyOneJob(stage),
|
||||
'gl-mr-26': shouldAddRightMargin(index),
|
||||
}"
|
||||
|
@ -231,7 +232,7 @@ export default {
|
|||
:groups="stage.groups"
|
||||
:stage-connector-class="stageConnectorClass(index, stage)"
|
||||
:is-first-column="isFirstColumn(index)"
|
||||
:has-triggered-by="hasTriggeredBy"
|
||||
:has-upstream="hasUpstream"
|
||||
:action="stage.status.action"
|
||||
:job-hovered="jobName"
|
||||
:pipeline-expanded="pipelineExpanded"
|
||||
|
@ -240,11 +241,11 @@ export default {
|
|||
</ul>
|
||||
|
||||
<linked-pipelines-column
|
||||
v-if="hasTriggered"
|
||||
:linked-pipelines="triggeredPipelines"
|
||||
v-if="hasDownstream"
|
||||
:type="$options.downstream"
|
||||
:linked-pipelines="downstreamPipelines"
|
||||
:column-title="__('Downstream')"
|
||||
:project-id="pipelineProjectId"
|
||||
graph-position="right"
|
||||
@linkedPipelineClick="handleClickedDownstream"
|
||||
@downstreamHovered="setJob"
|
||||
@pipelineExpandToggle="setPipelineExpanded"
|
||||
|
@ -252,15 +253,15 @@ export default {
|
|||
|
||||
<pipeline-graph
|
||||
v-if="pipelineTypeDownstream"
|
||||
type="downstream"
|
||||
:type="$options.downstream"
|
||||
class="d-inline-block"
|
||||
:class="`js-downstream-pipeline-${expandedTriggered.id}`"
|
||||
:class="`js-downstream-pipeline-${expandedDownstream.id}`"
|
||||
:is-loading="false"
|
||||
:pipeline="expandedTriggered"
|
||||
:pipeline="expandedDownstream"
|
||||
:is-linked-pipeline="true"
|
||||
:style="{ 'margin-top': downstreamMarginTop }"
|
||||
:mediator="mediator"
|
||||
@onClickTriggered="clickTriggeredPipeline"
|
||||
@onClickDownstreamPipeline="clickDownstreamPipeline"
|
||||
@refreshPipelineGraph="requestRefreshPipelineGraph"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { GlTooltipDirective, GlButton, GlLink, GlLoadingIcon } from '@gitlab/ui';
|
||||
import CiStatus from '~/vue_shared/components/ci_icon.vue';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { UPSTREAM, DOWNSTREAM } from './constants';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
|
@ -14,6 +15,10 @@ export default {
|
|||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
columnTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
pipeline: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -22,7 +27,7 @@ export default {
|
|||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
columnTitle: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
@ -50,12 +55,10 @@ export default {
|
|||
return this.childPipeline ? __('child-pipeline') : this.pipeline.project.name;
|
||||
},
|
||||
parentPipeline() {
|
||||
// Refactor string match when BE returns Upstream/Downstream indicators
|
||||
return this.projectId === this.pipeline.project.id && this.columnTitle === __('Upstream');
|
||||
return this.isUpstream && this.isSameProject;
|
||||
},
|
||||
childPipeline() {
|
||||
// Refactor string match when BE returns Upstream/Downstream indicators
|
||||
return this.projectId === this.pipeline.project.id && this.isDownstream;
|
||||
return this.isDownstream && this.isSameProject;
|
||||
},
|
||||
label() {
|
||||
if (this.parentPipeline) {
|
||||
|
@ -66,7 +69,13 @@ export default {
|
|||
return __('Multi-project');
|
||||
},
|
||||
isDownstream() {
|
||||
return this.columnTitle === __('Downstream');
|
||||
return this.type === DOWNSTREAM;
|
||||
},
|
||||
isUpstream() {
|
||||
return this.type === UPSTREAM;
|
||||
},
|
||||
isSameProject() {
|
||||
return this.projectId === this.pipeline.project.id;
|
||||
},
|
||||
sourceJobInfo() {
|
||||
return this.isDownstream
|
||||
|
@ -74,13 +83,13 @@ export default {
|
|||
: '';
|
||||
},
|
||||
expandedIcon() {
|
||||
if (this.parentPipeline) {
|
||||
if (this.isUpstream) {
|
||||
return this.expanded ? 'angle-right' : 'angle-left';
|
||||
}
|
||||
return this.expanded ? 'angle-left' : 'angle-right';
|
||||
},
|
||||
expandButtonPosition() {
|
||||
return this.parentPipeline ? 'gl-left-0 gl-border-r-1!' : 'gl-right-0 gl-border-l-1!';
|
||||
return this.isUpstream ? 'gl-left-0 gl-border-r-1!' : 'gl-right-0 gl-border-l-1!';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -116,7 +125,7 @@ export default {
|
|||
>
|
||||
<div
|
||||
class="gl-relative gl-bg-white gl-p-3 gl-border-solid gl-border-gray-100 gl-border-1"
|
||||
:class="{ 'gl-pl-9': parentPipeline }"
|
||||
:class="{ 'gl-pl-9': isUpstream }"
|
||||
>
|
||||
<div class="gl-display-flex">
|
||||
<ci-status
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import LinkedPipeline from './linked_pipeline.vue';
|
||||
import { __ } from '~/locale';
|
||||
import { UPSTREAM } from './constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -15,7 +15,7 @@ export default {
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
graphPosition: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
@ -32,9 +32,12 @@ export default {
|
|||
};
|
||||
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
|
||||
},
|
||||
graphPosition() {
|
||||
return this.isUpstream ? 'left' : 'right';
|
||||
},
|
||||
// Refactor string match when BE returns Upstream/Downstream indicators
|
||||
isUpstream() {
|
||||
return this.columnTitle === __('Upstream');
|
||||
return this.type === UPSTREAM;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -45,6 +48,11 @@ export default {
|
|||
this.$emit('downstreamHovered', jobName);
|
||||
},
|
||||
onPipelineExpandToggle(jobName, expanded) {
|
||||
// Highlighting only applies to downstream pipelines
|
||||
if (this.isUpstream) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('pipelineExpandToggle', jobName, expanded);
|
||||
},
|
||||
},
|
||||
|
@ -66,6 +74,7 @@ export default {
|
|||
:pipeline="pipeline"
|
||||
:column-title="columnTitle"
|
||||
:project-id="projectId"
|
||||
:type="type"
|
||||
@pipelineClicked="onPipelineClick($event, pipeline, index)"
|
||||
@downstreamHovered="onDownstreamHovered"
|
||||
@pipelineExpandToggle="onPipelineExpandToggle"
|
||||
|
|
|
@ -41,13 +41,13 @@ export default {
|
|||
this.mediator.poll.enable({ data: this.mediator.getExpandedParameters() });
|
||||
}
|
||||
},
|
||||
resetTriggeredPipelines(parentPipeline, pipeline) {
|
||||
resetDownstreamPipelines(parentPipeline, pipeline) {
|
||||
this.mediator.store.resetTriggeredPipelines(parentPipeline, pipeline);
|
||||
},
|
||||
clickTriggeredByPipeline(pipeline) {
|
||||
clickUpstreamPipeline(pipeline) {
|
||||
this.clickPipeline(pipeline, 'openPipeline', 'closePipeline');
|
||||
},
|
||||
clickTriggeredPipeline(pipeline) {
|
||||
clickDownstreamPipeline(pipeline) {
|
||||
this.clickPipeline(pipeline, 'openPipeline', 'closePipeline');
|
||||
},
|
||||
requestRefreshPipelineGraph() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
props: {
|
||||
hasTriggeredBy: {
|
||||
hasUpstream: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
|
@ -8,7 +8,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
buildConnnectorClass(index) {
|
||||
return index === 0 && (!this.isFirstColumn || this.hasTriggeredBy) ? 'left-connector' : '';
|
||||
return index === 0 && (!this.isFirstColumn || this.hasUpstream) ? 'left-connector' : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -45,10 +45,10 @@ const createLegacyPipelinesDetailApp = mediator => {
|
|||
},
|
||||
on: {
|
||||
refreshPipelineGraph: this.requestRefreshPipelineGraph,
|
||||
onResetTriggered: (parentPipeline, pipeline) =>
|
||||
this.resetTriggeredPipelines(parentPipeline, pipeline),
|
||||
onClickTriggeredBy: pipeline => this.clickTriggeredByPipeline(pipeline),
|
||||
onClickTriggered: pipeline => this.clickTriggeredPipeline(pipeline),
|
||||
onResetDownstream: (parentPipeline, pipeline) =>
|
||||
this.resetDownstreamPipelines(parentPipeline, pipeline),
|
||||
onClickUpstreamPipeline: pipeline => this.clickUpstreamPipeline(pipeline),
|
||||
onClickDownstreamPipeline: pipeline => this.clickDownstreamPipeline(pipeline),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -18,29 +18,32 @@ export default class PrometheusMetrics {
|
|||
this.$monitoredMetricsList = this.$monitoredMetricsPanel.find('.js-metrics-list');
|
||||
|
||||
this.$missingEnvVarPanel = this.$wrapper.find('.js-panel-missing-env-vars');
|
||||
this.$panelToggle = this.$missingEnvVarPanel.find('.js-panel-toggle');
|
||||
this.$panelToggleRight = this.$missingEnvVarPanel.find('.js-panel-toggle-right');
|
||||
this.$panelToggleDown = this.$missingEnvVarPanel.find('.js-panel-toggle-down');
|
||||
this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count');
|
||||
this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list');
|
||||
|
||||
this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('activeMetrics');
|
||||
this.helpMetricsPath = this.$monitoredMetricsPanel.data('metrics-help-path');
|
||||
|
||||
this.$panelToggle.on('click', e => this.handlePanelToggle(e));
|
||||
this.$panelToggleRight.on('click', e => this.handlePanelToggle(e));
|
||||
this.$panelToggleDown.on('click', e => this.handlePanelToggle(e));
|
||||
}
|
||||
|
||||
init() {
|
||||
this.loadActiveMetrics();
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
handlePanelToggle(e) {
|
||||
const $toggleBtn = $(e.currentTarget);
|
||||
const $currentPanelBody = $toggleBtn.closest('.card').find('.card-body');
|
||||
$currentPanelBody.toggleClass('hidden');
|
||||
if ($toggleBtn.hasClass('fa-caret-down')) {
|
||||
$toggleBtn.removeClass('fa-caret-down').addClass('fa-caret-right');
|
||||
} else {
|
||||
$toggleBtn.removeClass('fa-caret-right').addClass('fa-caret-down');
|
||||
if ($toggleBtn.hasClass('js-panel-toggle-right')) {
|
||||
$toggleBtn.addClass('hidden');
|
||||
this.$panelToggleDown.removeClass('hidden');
|
||||
} else if ($toggleBtn.hasClass('js-panel-toggle-down')) {
|
||||
$toggleBtn.addClass('hidden');
|
||||
this.$panelToggleRight.removeClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,8 @@ import { GlIcon } from '@gitlab/ui';
|
|||
*
|
||||
* Receives status object containing:
|
||||
* status: {
|
||||
* details_path: "/gitlab-org/gitlab-foss/pipelines/8150156" // url
|
||||
* group:"running" // used for CSS class
|
||||
* icon: "icon_status_running" // used to render the icon
|
||||
* label:"running" // used for potential tooltip
|
||||
* text:"running" // text rendered
|
||||
* }
|
||||
*
|
||||
* Used in:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { uniqueId } from 'lodash';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
|
||||
|
@ -7,6 +7,9 @@ export default {
|
|||
components: {
|
||||
GlModal,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
props: {
|
||||
selector: {
|
||||
type: String,
|
||||
|
@ -71,7 +74,8 @@ export default {
|
|||
-->
|
||||
<input type="hidden" name="_method" :value="method" />
|
||||
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
|
||||
<div>{{ modalAttributes.message }}</div>
|
||||
<div v-if="modalAttributes.messageHtml" v-safe-html="modalAttributes.messageHtml"></div>
|
||||
<div v-else>{{ modalAttributes.message }}</div>
|
||||
</form>
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
# rubocop: disable Graphql/ResolverType
|
||||
|
||||
module Resolvers
|
||||
class CommitPipelinesResolver < BaseResolver
|
||||
# The GraphQL type here gets defined in this include
|
||||
include ::ResolvesPipelines
|
||||
|
||||
alias_method :commit, :object
|
||||
|
@ -11,3 +13,4 @@ module Resolvers
|
|||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Graphql/ResolverType
|
||||
|
|
|
@ -4,7 +4,7 @@ module ResolvesPipelines
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
type [Types::Ci::PipelineType], null: false
|
||||
type Types::Ci::PipelineType.connection_type, null: false
|
||||
argument :status,
|
||||
Types::Ci::PipelineStatusEnum,
|
||||
required: false,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
# rubocop: disable Graphql/ResolverType
|
||||
|
||||
module Resolvers
|
||||
class MergeRequestPipelinesResolver < BaseResolver
|
||||
# The GraphQL type here gets defined in this include
|
||||
include ::ResolvesPipelines
|
||||
|
||||
alias_method :merge_request, :object
|
||||
|
@ -18,3 +20,4 @@ module Resolvers
|
|||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Graphql/ResolverType
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
# The GraphQL type here gets defined in
|
||||
# https://gitlab.com/gitlab-org/gitlab/blob/master/app/graphql/resolvers/concerns/resolves_pipelines.rb#L7
|
||||
# rubocop: disable Graphql/ResolverType
|
||||
|
||||
module Resolvers
|
||||
class ProjectPipelinesResolver < BaseResolver
|
||||
|
@ -22,3 +25,4 @@ module Resolvers
|
|||
end
|
||||
end
|
||||
end
|
||||
# rubocop: enable Graphql/ResolverType
|
||||
|
|
|
@ -40,7 +40,7 @@ module Types
|
|||
field :author, type: Types::UserType, null: true,
|
||||
description: 'Author of the commit'
|
||||
|
||||
field :pipelines, Types::Ci::PipelineType.connection_type,
|
||||
field :pipelines,
|
||||
null: true,
|
||||
description: 'Pipelines of the commit ordered latest first',
|
||||
resolver: Resolvers::CommitPipelinesResolver
|
||||
|
|
|
@ -114,7 +114,7 @@ module Types
|
|||
|
||||
field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline,
|
||||
description: 'The pipeline running on the branch HEAD of the merge request'
|
||||
field :pipelines, Types::Ci::PipelineType.connection_type,
|
||||
field :pipelines,
|
||||
null: true,
|
||||
description: 'Pipelines for the merge request',
|
||||
resolver: Resolvers::MergeRequestPipelinesResolver
|
||||
|
|
|
@ -187,7 +187,6 @@ module Types
|
|||
resolver: Resolvers::PackagesResolver
|
||||
|
||||
field :pipelines,
|
||||
Types::Ci::PipelineType.connection_type,
|
||||
null: true,
|
||||
description: 'Build pipelines of the project',
|
||||
extras: [:lookahead],
|
||||
|
|
|
@ -110,6 +110,32 @@ module UsersHelper
|
|||
!user.confirmed?
|
||||
end
|
||||
|
||||
def user_block_data(user, message)
|
||||
{
|
||||
path: block_admin_user_path(user),
|
||||
method: 'put',
|
||||
modal_attributes: {
|
||||
title: s_('AdminUsers|Block user %{username}?') % { username: sanitize_name(user.name) },
|
||||
messageHtml: message,
|
||||
okVariant: 'warning',
|
||||
okTitle: s_('AdminUsers|Block')
|
||||
}.to_json
|
||||
}
|
||||
end
|
||||
|
||||
def user_block_effects
|
||||
header = tag.p s_('AdminUsers|Blocking user has the following effects:')
|
||||
|
||||
list = tag.ul do
|
||||
concat tag.li s_('AdminUsers|User will not be able to login')
|
||||
concat tag.li s_('AdminUsers|User will not be able to access git repositories')
|
||||
concat tag.li s_('AdminUsers|Personal projects will be left')
|
||||
concat tag.li s_('AdminUsers|Owned groups will be left')
|
||||
end
|
||||
|
||||
header + list
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def blocked_user_badge(user)
|
||||
|
|
|
@ -39,8 +39,6 @@ module Ci
|
|||
end
|
||||
|
||||
def track_test_cases(build, test_suite)
|
||||
return if Feature.disabled?(:track_unique_test_cases_parsed, build.project)
|
||||
|
||||
track_usage_event(EVENT_NAME, test_case_hashes(build, test_suite))
|
||||
end
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ module Snippets
|
|||
|
||||
snippet_saved
|
||||
rescue => e # Rescuing all because we can receive Creation exceptions, GRPC exceptions, Git exceptions, ...
|
||||
log_error(e.message)
|
||||
Gitlab::ErrorTracking.log_exception(e, service: 'Snippets::CreateService')
|
||||
|
||||
# If the commit action failed we need to remove the repository if exists
|
||||
delete_repository(@snippet) if @snippet.repository_exists?
|
||||
|
|
|
@ -74,7 +74,7 @@ module Snippets
|
|||
|
||||
add_snippet_repository_error(snippet: snippet, error: e)
|
||||
|
||||
log_error(e.message)
|
||||
Gitlab::ErrorTracking.log_exception(e, service: 'Snippets::UpdateService')
|
||||
|
||||
# If the commit action failed we remove it because
|
||||
# we don't want to leave empty repositories
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
.card-header.bg-warning.text-white
|
||||
= s_('AdminUsers|Block this user')
|
||||
.card-body
|
||||
= render partial: 'admin/users/user_block_effects'
|
||||
= user_block_effects
|
||||
%br
|
||||
%button.btn.gl-button.btn-warning{ data: { 'gl-modal-action': 'block',
|
||||
content: s_('AdminUsers|You can always unblock their account, their data will remain intact.'),
|
||||
url: block_admin_user_path(user),
|
||||
username: sanitize_name(user.name) } }
|
||||
%button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_block_data(user, s_('AdminUsers|You can always unblock their account, their data will remain intact.')) }
|
||||
= s_('AdminUsers|Block user')
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
action: s_("AdminUsers|Deactivate") } }
|
||||
= render partial: 'admin/users/user_deactivation_effects'
|
||||
|
||||
%div{ data: { modal: "block",
|
||||
title: s_("AdminUsers|Block user %{username}?"),
|
||||
action: s_("AdminUsers|Block") } }
|
||||
= render partial: 'admin/users/user_block_effects'
|
||||
|
||||
%div{ data: { modal: "delete",
|
||||
title: s_("AdminUsers|Delete User %{username}?"),
|
||||
action: s_('AdminUsers|Delete user'),
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
.table-action-buttons
|
||||
= link_to _('Edit'), edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn gl-button btn-default'
|
||||
- unless user == current_user
|
||||
%button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { toggle: 'dropdown' } }
|
||||
%button.dropdown-new.btn.gl-button.btn-default{ type: 'button', data: { testid: "user-action-button-#{user.id}", toggle: 'dropdown' } }
|
||||
= sprite_icon('settings')
|
||||
= sprite_icon('chevron-down')
|
||||
%ul.dropdown-menu.dropdown-menu-right
|
||||
%ul.dropdown-menu.dropdown-menu-right{ data: { testid: "user-action-dropdown-#{user.id}" } }
|
||||
%li.dropdown-header
|
||||
= _('Settings')
|
||||
%li
|
||||
|
@ -37,16 +37,12 @@
|
|||
- elsif user.blocked?
|
||||
- if user.blocked_pending_approval?
|
||||
= link_to s_('AdminUsers|Approve'), approve_admin_user_path(user), method: :put
|
||||
%button.btn.btn-default-tertiary{ data: { 'gl-modal-action': 'block',
|
||||
url: block_admin_user_path(user),
|
||||
username: sanitize_name(user.name) } }
|
||||
%button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
|
||||
= s_('AdminUsers|Block')
|
||||
- else
|
||||
= link_to _('Unblock'), unblock_admin_user_path(user), method: :put
|
||||
- else
|
||||
%button.btn.btn-default-tertiary{ data: { 'gl-modal-action': 'block',
|
||||
url: block_admin_user_path(user),
|
||||
username: sanitize_name(user.name) } }
|
||||
%button.btn.btn-default-tertiary.js-confirm-modal-button{ data: user_block_data(user, user_block_effects) }
|
||||
= s_('AdminUsers|Block')
|
||||
- if user.can_be_deactivated?
|
||||
%li
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
%p
|
||||
= s_('AdminUsers|Blocking user has the following effects:')
|
||||
%ul
|
||||
%li
|
||||
= s_('AdminUsers|User will not be able to login')
|
||||
%li
|
||||
= s_('AdminUsers|User will not be able to access git repositories')
|
||||
%li
|
||||
= s_('AdminUsers|Personal projects will be left')
|
||||
%li
|
||||
= s_('AdminUsers|Owned groups will be left')
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
= form_for @project, url: remove_fork_project_path(@project), method: :delete, remote: true, html: { class: 'transfer-project' } do |f|
|
||||
%p
|
||||
%strong= _('Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.')
|
||||
%strong= _('Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks.')
|
||||
= button_to _('Remove fork relationship'), '#', class: "gl-button btn btn-danger js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_warning_message(@project) }
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
|
||||
.card.hidden.js-panel-missing-env-vars
|
||||
.card-header
|
||||
= icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel')
|
||||
= sprite_icon('chevron-lg-right', css_class: 'panel-toggle js-panel-toggle-right' )
|
||||
= sprite_icon('chevron-lg-down', css_class: 'panel-toggle js-panel-toggle-down hidden' )
|
||||
= s_('PrometheusService|Missing environment variable')
|
||||
%span.badge.badge-pill.js-env-var-count 0
|
||||
.card-body.hidden
|
||||
|
|
|
@ -119,6 +119,9 @@
|
|||
%li
|
||||
pytest-cov (Python) -
|
||||
%code ^TOTAL.+?(\d+\%)$
|
||||
%li
|
||||
Scoverage (Scala) -
|
||||
%code Statement coverage[A-Za-z\.*]\s*:\s*([^%]+)
|
||||
%li
|
||||
phpunit --coverage-text --colors=never (PHP) -
|
||||
%code ^\s*Lines:\s*\d+.\d+\%
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove columns no longer used for replicating terraform state
|
||||
merge_request: 46742
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change wording on the project remove fork page
|
||||
merge_request: 47878
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove feature flag to enable tracking unique test cases parsed globally.
|
||||
merge_request: 47662
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/feat-scoverage-ci-regex.yml
Normal file
5
changelogs/unreleased/feat-scoverage-ci-regex.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Added code coverage regex for Scala Scoverage
|
||||
merge_request: 46638
|
||||
author: opensorceror
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace font-awesome icons in prometheus config
|
||||
merge_request: 47713
|
||||
author:
|
||||
type: changed
|
5
changelogs/unreleased/migrate-terminal-alert.yml
Normal file
5
changelogs/unreleased/migrate-terminal-alert.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update terminal empty state alert to gl component
|
||||
merge_request: 47340
|
||||
author:
|
||||
type: other
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: track_unique_test_cases_parsed
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41918
|
||||
rollout_issue_url:
|
||||
milestone: '13.5'
|
||||
type: development
|
||||
group: group::testing
|
||||
default_enabled: false
|
|
@ -557,10 +557,6 @@ module.exports = {
|
|||
port: DEV_SERVER_PORT,
|
||||
public: DEV_SERVER_PUBLIC_ADDR,
|
||||
https: DEV_SERVER_HTTPS,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': '*',
|
||||
},
|
||||
contentBase: false,
|
||||
stats: 'errors-only',
|
||||
hot: DEV_SERVER_LIVERELOAD,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveTerraformStateVerificationColumns < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
transaction do
|
||||
remove_column :terraform_states, :verification_retry_at, :datetime_with_timezone
|
||||
remove_column :terraform_states, :verified_at, :datetime_with_timezone
|
||||
remove_column :terraform_states, :verification_retry_count, :integer, limit: 2
|
||||
remove_column :terraform_states, :verification_checksum, :binary, using: 'verification_checksum::bytea'
|
||||
remove_column :terraform_states, :verification_failure, :text
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
add_column(:terraform_states, :verification_retry_at, :datetime_with_timezone) unless column_exists?(:terraform_states, :verification_retry_at)
|
||||
add_column(:terraform_states, :verified_at, :datetime_with_timezone) unless column_exists?(:terraform_states, :verified_at)
|
||||
add_column(:terraform_states, :verification_retry_count, :integer, limit: 2) unless column_exists?(:terraform_states, :verification_retry_count)
|
||||
add_column(:terraform_states, :verification_checksum, :binary, using: 'verification_checksum::bytea') unless column_exists?(:terraform_states, :verification_checksum)
|
||||
add_column(:terraform_states, :verification_failure, :text) unless column_exists?(:terraform_states, :verification_failure)
|
||||
|
||||
add_text_limit :terraform_states, :verification_failure, 255
|
||||
end
|
||||
end
|
1
db/schema_migrations/20201103013242
Normal file
1
db/schema_migrations/20201103013242
Normal file
|
@ -0,0 +1 @@
|
|||
8a30cf20f71e0cb198caf6f77a19e4da55b83eb38bdb5888cd2674373d94cede
|
|
@ -16623,13 +16623,7 @@ CREATE TABLE terraform_states (
|
|||
locked_by_user_id bigint,
|
||||
uuid character varying(32) NOT NULL,
|
||||
name character varying(255),
|
||||
verification_retry_at timestamp with time zone,
|
||||
verified_at timestamp with time zone,
|
||||
verification_retry_count smallint,
|
||||
verification_checksum bytea,
|
||||
verification_failure text,
|
||||
versioning_enabled boolean DEFAULT false NOT NULL,
|
||||
CONSTRAINT check_21a47163ea CHECK ((char_length(verification_failure) <= 255))
|
||||
versioning_enabled boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE terraform_states_id_seq
|
||||
|
|
|
@ -127,8 +127,11 @@ and verification status on a **secondary** node.
|
|||
You can keep track of the progress to implement the missing items in
|
||||
these epics/issues:
|
||||
|
||||
- [Unreplicated Data Types](https://gitlab.com/groups/gitlab-org/-/epics/893)
|
||||
- [Verify all replicated data](https://gitlab.com/groups/gitlab-org/-/epics/1430)
|
||||
- [Geo: Build a scalable, self-service Geo replication and verification framework](https://gitlab.com/groups/gitlab-org/-/epics/2161)
|
||||
- [Geo: Improve the self-service Geo replication framework](https://gitlab.com/groups/gitlab-org/-/epics/3761)
|
||||
- [Geo: Move existing blobs to framework](https://gitlab.com/groups/gitlab-org/-/epics/3588)
|
||||
- [Geo: Add unreplicated data types](https://gitlab.com/groups/gitlab-org/-/epics/893)
|
||||
- [Geo: Support GitLab Pages](https://gitlab.com/groups/gitlab-org/-/epics/589)
|
||||
|
||||
### Replicated data types behind a feature flag
|
||||
|
||||
|
|
|
@ -553,7 +553,6 @@ database encryption. Proceed with caution.
|
|||
1. On the **GitLab server**, to enable Pages, add the following to `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_pages['enable'] = true
|
||||
pages_external_url "http://<pages_server_URL>"
|
||||
```
|
||||
|
||||
|
@ -584,18 +583,11 @@ database encryption. Proceed with caution.
|
|||
to include:
|
||||
|
||||
```ruby
|
||||
external_url 'http://<gitlab_server_IP_or_URL>'
|
||||
roles ['pages_role']
|
||||
|
||||
pages_external_url "http://<pages_server_URL>"
|
||||
postgresql['enable'] = false
|
||||
redis['enable'] = false
|
||||
prometheus['enable'] = false
|
||||
puma['enable'] = false
|
||||
sidekiq['enable'] = false
|
||||
gitlab_workhorse['enable'] = false
|
||||
gitaly['enable'] = false
|
||||
alertmanager['enable'] = false
|
||||
node_exporter['enable'] = false
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
||||
gitlab_pages['gitlab_server'] = 'http://<gitlab_server_IP_or_URL>'
|
||||
```
|
||||
|
||||
1. Create a backup of the secrets file on the **Pages server**:
|
||||
|
|
|
@ -72,9 +72,11 @@ Only API version v4 is available. Version v3 was removed in
|
|||
|
||||
API requests should be prefixed with both `api` and the API version. The API
|
||||
version is defined in [`lib/api.rb`](https://gitlab.com/gitlab-org/gitlab/tree/master/lib/api/api.rb).
|
||||
For example, the root of the v4 API is at `/api/v4`.
|
||||
For example, the root of the v4 API is at `/api/v4`. The following sections illustrate different uses:
|
||||
|
||||
Example of a valid API request using cURL:
|
||||
### Valid API request
|
||||
|
||||
If you have a GitLab instance at `gitlab.example.com`:
|
||||
|
||||
```shell
|
||||
curl "https://gitlab.example.com/api/v4/projects"
|
||||
|
@ -83,6 +85,29 @@ curl "https://gitlab.example.com/api/v4/projects"
|
|||
The API uses JSON to serialize data. You don't need to specify `.json` at the
|
||||
end of an API URL.
|
||||
|
||||
### API request to expose HTTP response headers
|
||||
|
||||
If you want to expose HTTP response headers, use the `--include` option:
|
||||
|
||||
```shell
|
||||
curl --include "https://gitlab.example.com/api/v4/projects"
|
||||
HTTP/2 200
|
||||
...
|
||||
```
|
||||
|
||||
This can help you investigate an unexpected response.
|
||||
|
||||
### API Request that includes the exit code
|
||||
|
||||
If you want to expose the HTTP exit code, include the `--fail` option:
|
||||
|
||||
```shell script
|
||||
curl --fail "https://gitlab.example.com/api/v4/does-not-exist"
|
||||
curl: (22) The requested URL returned error: 404
|
||||
```
|
||||
|
||||
The HTTP exit code can help you diagnose the success or failure of your REST call.
|
||||
|
||||
## Authentication
|
||||
|
||||
Most API requests require authentication, or will return public data only when
|
||||
|
|
|
@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/269) in GitLab 13.2.
|
||||
|
||||
Each Sidekiq worker, controller action, or (eventually) API endpoint
|
||||
Each Sidekiq worker, controller action, or API endpoint
|
||||
must declare a `feature_category` attribute. This attribute maps each
|
||||
of these to a [feature
|
||||
category](https://about.gitlab.com/handbook/product/product-categories/). This
|
||||
|
@ -118,3 +118,42 @@ assigned to all actions.
|
|||
|
||||
The spec also validates if the used feature categories are known. And if
|
||||
the actions used in configuration still exist as routes.
|
||||
|
||||
## API endpoints
|
||||
|
||||
Grape API endpoints can use the `feature_category` class method, like
|
||||
[Rails controllers](#rails-controllers) do:
|
||||
|
||||
```ruby
|
||||
module API
|
||||
class Issues < ::API::Base
|
||||
feature_category :issue_tracking
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The second argument can be used to specify feature categories for
|
||||
specific routes:
|
||||
|
||||
```ruby
|
||||
module API
|
||||
class Users < ::API::Base
|
||||
feature_category :users, ['/users/:id/custom_attributes', '/users/:id/custom_attributes/:key']
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Or the feature category can be specified in the action itself:
|
||||
|
||||
```ruby
|
||||
module API
|
||||
class Users < ::API::Base
|
||||
get ':id', feature_category: :users do
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
As with Rails controllers, an API class must specify the category for
|
||||
every single action unless the same category is used for every action
|
||||
within that class.
|
||||
|
|
|
@ -735,7 +735,8 @@ In order to add data for aggregated metrics into Usage Ping payload you should a
|
|||
|
||||
- name: unique name under which aggregate metric will be added to Usage Ping payload
|
||||
- operator: operator that defines how aggregated metric data will be counted. Available operators are:
|
||||
- `ANY`: removes duplicates and counts all entries that triggered any of listed events
|
||||
- `OR`: removes duplicates and counts all entries that triggered any of listed events
|
||||
- `AND`: removes duplicates and counts all elements that were observed triggering all of following events
|
||||
- events: list of events names (from [`known_events.yml`](#known-events-in-usage-data-payload)) to aggregate into metric. All events in this list must have the same `redis_slot` and `aggregation` attributes.
|
||||
- feature_flag: name of [development feature flag](../feature_flags/development.md#development-type) that will be checked before
|
||||
metrics aggregation is performed. Corresponding feature flag should have `default_enabled` attribute set to `false`.
|
||||
|
@ -744,11 +745,11 @@ metrics aggregation is performed. Corresponding feature flag should have `defaul
|
|||
Example aggregated metric entries:
|
||||
|
||||
```yaml
|
||||
- name: example_aggregated_metric
|
||||
operator: ANY
|
||||
events: ['i_search_advanced', 'i_search_paid']
|
||||
- name: example_aggregated_metric_with_feautre_flag
|
||||
operator: ANY
|
||||
- name: product_analytics_test_metrics_union
|
||||
operator: OR
|
||||
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
|
||||
- name: product_analytics_test_metrics_intersection_with_feautre_flag
|
||||
operator: AND
|
||||
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
|
||||
feature_flag: example_aggregated_metric
|
||||
```
|
||||
|
@ -766,7 +767,8 @@ Aggregated metrics will be added under `aggregated_metrics` key in both `counts_
|
|||
:project_snippets => 407,
|
||||
:promoted_issues => 719,
|
||||
:aggregated_metrics => {
|
||||
:example_aggregated_metric => 7
|
||||
:product_analytics_test_metrics_union => 7,
|
||||
:product_analytics_test_metrics_intersection_with_feautre_flag => 2
|
||||
},
|
||||
:snippets => 2513
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ group: Static Analysis
|
|||
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
|
||||
---
|
||||
|
||||
# SAST Analyzers **(ULTIMATE)**
|
||||
# SAST Analyzers **(CORE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3775) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.3.
|
||||
> - [Moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to GitLab Core in 13.3.
|
||||
|
||||
SAST relies on underlying third party tools that are wrapped into what we call
|
||||
"Analyzers". An analyzer is a
|
||||
|
@ -129,11 +132,11 @@ The [Security Scanner Integration](../../../development/integrations/secure.md)
|
|||
| End line | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Start column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 |
|
||||
| End column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| External ID (e.g. CVE) | 𐄂 | 𐄂 | ⚠ | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| External ID (for example, CVE) | 𐄂 | 𐄂 | ⚠ | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| URLs | ✓ | 𐄂 | ✓ | 𐄂 | ⚠ | 𐄂 | ⚠ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Internal doc/explanation | ✓ | ⚠ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
|
||||
| Solution | ✓ | 𐄂 | 𐄂 | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Affected item (e.g. class or package) | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Affected item (for example, class or package) | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Confidence | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | x | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
|
||||
| Source code extract | 𐄂 | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
|
||||
| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | ✓ | ✓ | ✓ |
|
||||
|
@ -143,4 +146,4 @@ The [Security Scanner Integration](../../../development/integrations/secure.md)
|
|||
- 𐄂 => we don't have that data or it would need to develop specific or inefficient/unreliable logic to obtain it.
|
||||
|
||||
The values provided by these tools are heterogeneous so they are sometimes
|
||||
normalized into common values (e.g., `severity`, `confidence`, etc).
|
||||
normalized into common values (for example, `severity`, `confidence`, and so on).
|
||||
|
|
|
@ -160,6 +160,25 @@ gitops:
|
|||
...
|
||||
```
|
||||
|
||||
GitLab [versions 13.6 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/259669) also
|
||||
supports manifest projects containing multiple directories (or subdirectories)
|
||||
of YAML files. To use multiple YAML files, specify a `paths` attribute:
|
||||
|
||||
```yaml
|
||||
gitops:
|
||||
manifest_projects:
|
||||
- id: "path-to/your-manifest-project-number1"
|
||||
paths:
|
||||
# Read all .yaml files from team1/app1 directory.
|
||||
# See https://github.com/bmatcuk/doublestar#about and
|
||||
# https://pkg.go.dev/github.com/bmatcuk/doublestar/v2#Match for globbing rules.
|
||||
- glob: '/team1/app1/*.yaml'
|
||||
# Read all .yaml files from team2/apps and all subdirectories
|
||||
- glob: '/team2/apps/**/*.yaml'
|
||||
# If 'paths' is not specified or is an empty list, the configuration below is used
|
||||
- glob: '/**/*.{yaml,yml,json}'
|
||||
```
|
||||
|
||||
### Create an Agent record in GitLab
|
||||
|
||||
Next, create an GitLab Rails Agent record so the Agent can associate itself with
|
||||
|
|
|
@ -107,7 +107,7 @@ The chart used to install this application depends on the version of GitLab used
|
|||
- GitLab 12.3 and newer, the [`jetstack/cert-manager`](https://github.com/jetstack/cert-manager)
|
||||
chart is used with a [`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/cert_manager/values.yaml)
|
||||
file.
|
||||
- GitLab 12.2 and older, the [`stable/cert-manager`](https://gi2wthub.com/helm/charts/tree/master/stable/cert-manager)
|
||||
- GitLab 12.2 and older, the [`stable/cert-manager`](https://github.com/helm/charts/tree/master/stable/cert-manager)
|
||||
chart was used.
|
||||
|
||||
If you installed cert-manager prior to GitLab 12.3, Let's Encrypt
|
||||
|
@ -775,7 +775,7 @@ certManager:
|
|||
You can customize the installation of cert-manager by defining a
|
||||
`.gitlab/managed-apps/cert-manager/values.yaml` file in your cluster
|
||||
management project. Refer to the
|
||||
[chart](https://hub.helm.sh/charts/jetstack/cert-manager) for the
|
||||
[chart](https://github.com/jetstack/cert-manager) for the
|
||||
available configuration options.
|
||||
|
||||
Support for installing the Cert Manager managed application is provided by the
|
||||
|
@ -855,7 +855,7 @@ least 2 people from the
|
|||
|
||||
### Install PostHog using GitLab CI/CD
|
||||
|
||||
[PostHog](https://www.posthog.com) 🦔 is a developer-friendly, open-source product analytics platform.
|
||||
[PostHog](https://posthog.com) 🦔 is a developer-friendly, open-source product analytics platform.
|
||||
|
||||
To install PostHog into the `gitlab-managed-apps` namespace of your cluster,
|
||||
define the `.gitlab/managed-apps/config.yaml` file with:
|
||||
|
@ -1012,15 +1012,15 @@ CAUTION: **Caution:**
|
|||
Installation and removal of the Cilium requires a **manual**
|
||||
[restart](https://docs.cilium.io/en/stable/gettingstarted/k8s-install-gke/#restart-unmanaged-pods)
|
||||
of all affected pods in all namespaces to ensure that they are
|
||||
[managed](https://docs.cilium.io/en/stable/troubleshooting/#ensure-pod-is-managed-by-cilium)
|
||||
[managed](https://docs.cilium.io/en/v1.8/operations/troubleshooting/#ensure-managed-pod)
|
||||
by the correct networking plugin.
|
||||
|
||||
NOTE: **Note:**
|
||||
Major upgrades might require additional setup steps. For more information, see
|
||||
the official [upgrade guide](https://docs.cilium.io/en/stable/install/upgrade/).
|
||||
the official [upgrade guide](https://docs.cilium.io/en/v1.8/operations/upgrade/).
|
||||
|
||||
By default, Cilium's
|
||||
[audit mode](https://docs.cilium.io/en/v1.8/gettingstarted/policy-creation/?highlight=policy-audit#enable-policy-audit-mode)
|
||||
[audit mode](https://docs.cilium.io/en/v1.8/gettingstarted/policy-creation/#enable-policy-audit-mode)
|
||||
is enabled. In audit mode, Cilium doesn't drop disallowed packets. You
|
||||
can use `policy-verdict` log to observe policy-related decisions. You
|
||||
can disable audit mode by adding the following to
|
||||
|
|
|
@ -152,7 +152,7 @@ Shared runners provided by GitLab are **not** configurable. Consider [installing
|
|||
Linux shared runners on GitLab.com run in [autoscale mode](https://docs.gitlab.com/runner/configuration/autoscale.html) and are powered by Google Cloud Platform.
|
||||
Autoscaling means reduced waiting times to spin up CI/CD jobs, and isolated VMs for each project,
|
||||
thus maximizing security. They're free to use for public open source projects and limited
|
||||
to 2000 CI minutes per month per group for private projects. More minutes
|
||||
to 400 CI minutes per month per group for private projects. More minutes
|
||||
[can be purchased](../../subscriptions/gitlab_com/index.md#purchase-additional-ci-minutes), if
|
||||
needed. Read about all [GitLab.com plans](https://about.gitlab.com/pricing/).
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ name or a referenced merge request or your project has an active
|
|||
fork relationship.
|
||||
If you would like to make this button appear, a possible workaround is to [remove your project's
|
||||
fork relationship](../settings/index.md#removing-a-fork-relationship). Once removed, the fork
|
||||
relationship cannot be restored, and you will no longer be able to send merge requests to the source.
|
||||
relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks.
|
||||
|
||||
![Create Button](img/web_editor_new_branch_from_issue_create_button_v12_6.png)
|
||||
|
||||
|
|
|
@ -13,9 +13,8 @@ module Gitlab
|
|||
'repositories/git_http' => %w{git_upload_pack}
|
||||
}.freeze
|
||||
|
||||
ALLOWLISTED_GIT_LFS_ROUTES = {
|
||||
'repositories/lfs_api' => %w{batch},
|
||||
'repositories/lfs_locks_api' => %w{verify create unlock}
|
||||
ALLOWLISTED_GIT_LFS_BATCH_ROUTES = {
|
||||
'repositories/lfs_api' => %w{batch}
|
||||
}.freeze
|
||||
|
||||
ALLOWLISTED_GIT_REVISION_ROUTES = {
|
||||
|
@ -88,7 +87,7 @@ module Gitlab
|
|||
|
||||
# Overridden in EE module
|
||||
def allowlisted_routes
|
||||
workhorse_passthrough_route? || internal_route? || lfs_route? || compare_git_revisions_route? || sidekiq_route? || session_route? || graphql_query?
|
||||
workhorse_passthrough_route? || internal_route? || lfs_batch_route? || compare_git_revisions_route? || sidekiq_route? || session_route? || graphql_query?
|
||||
end
|
||||
|
||||
# URL for requests passed through gitlab-workhorse to rails-web
|
||||
|
@ -112,15 +111,13 @@ module Gitlab
|
|||
ALLOWLISTED_GIT_REVISION_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
|
||||
end
|
||||
|
||||
def lfs_route?
|
||||
# Batch upload requests are blocked in:
|
||||
# https://gitlab.com/gitlab-org/gitlab/blob/master/app/controllers/repositories/lfs_api_controller.rb#L106
|
||||
def lfs_batch_route?
|
||||
# Calling route_hash may be expensive. Only do it if we think there's a possible match
|
||||
unless request.path.end_with?('/info/lfs/objects/batch',
|
||||
'/info/lfs/locks', '/info/lfs/locks/verify') ||
|
||||
%r{/info/lfs/locks/\d+/unlock\z}.match?(request.path)
|
||||
return false
|
||||
end
|
||||
return unless request.path.end_with?('/info/lfs/objects/batch')
|
||||
|
||||
ALLOWLISTED_GIT_LFS_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
|
||||
ALLOWLISTED_GIT_LFS_BATCH_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
|
||||
end
|
||||
|
||||
def session_route?
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
#- name: unique name of aggregated metric
|
||||
# operator: aggregation operator. Valid values are:
|
||||
# - "ANY": counts unique elements that were observed triggering any of following events
|
||||
# - "ALL": counts unique elements that were observed triggering all of following events
|
||||
# - "OR": counts unique elements that were observed triggering any of following events
|
||||
# - "AND": counts unique elements that were observed triggering all of following events
|
||||
# events: list of events names to aggregate into metric. All events in this list must have the same 'redis_slot' and 'aggregation' attributes
|
||||
# see from lib/gitlab/usage_data_counters/known_events/ for the list of valid events.
|
||||
# feature_flag: name of development feature flag that will be checked before metrics aggregation is performed.
|
||||
# Corresponding feature flag should have `default_enabled` attribute set to `false`.
|
||||
# This attribute is OPTIONAL and can be omitted, when `feature_flag` is missing no feature flag will be checked.
|
||||
---
|
||||
- name: product_analytics_test_aggregated_metrics
|
||||
operator: ANY
|
||||
- name: product_analytics_test_metrics_union
|
||||
operator: OR
|
||||
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
|
||||
feature_flag: product_analytics_aggregated_metrics
|
||||
- name: product_analytics_test_combined_events
|
||||
operator: ALL
|
||||
- name: product_analytics_test_metrics_intersection
|
||||
operator: AND
|
||||
events: ['i_search_total', 'i_search_advanced', 'i_search_paid']
|
||||
feature_flag: product_analytics_aggregated_metrics
|
||||
|
|
|
@ -18,8 +18,8 @@ module Gitlab
|
|||
|
||||
KNOWN_EVENTS_PATH = File.expand_path('known_events/*.yml', __dir__)
|
||||
ALLOWED_AGGREGATIONS = %i(daily weekly).freeze
|
||||
UNION_OF_AGGREGATED_METRICS = 'ANY'
|
||||
INTERSECTION_OF_AGGREGATED_METRICS = 'ALL'
|
||||
UNION_OF_AGGREGATED_METRICS = 'OR'
|
||||
INTERSECTION_OF_AGGREGATED_METRICS = 'AND'
|
||||
ALLOWED_METRICS_AGGREGATIONS = [UNION_OF_AGGREGATED_METRICS, INTERSECTION_OF_AGGREGATED_METRICS].freeze
|
||||
AGGREGATED_METRICS_PATH = File.expand_path('aggregated_metrics/*.yml', __dir__)
|
||||
|
||||
|
|
|
@ -18967,7 +18967,7 @@ msgstr ""
|
|||
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
|
||||
msgid "Once removed, the fork relationship cannot be restored. This project will no longer be able to receive or send merge requests to the source project or other forks."
|
||||
msgstr ""
|
||||
|
||||
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
|
||||
|
|
|
@ -20,13 +20,21 @@ cp Dockerfile.assets assets_container.build/
|
|||
|
||||
COMMIT_REF_SLUG_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
|
||||
COMMIT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
|
||||
COMMIT_REF_NAME_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
|
||||
|
||||
DESTINATIONS="--destination=$COMMIT_REF_SLUG_DESTINATION --destination=$COMMIT_SHA_DESTINATION"
|
||||
|
||||
# For EE branch builds, add a truncated SHA destination for later use by Omnibus
|
||||
# auto-deploy builds
|
||||
if [[ "${ASSETS_IMAGE_NAME}" == "gitlab-assets-ee" ]] && [ -n "$CI_COMMIT_BRANCH" ]
|
||||
then
|
||||
COMMIT_SHORT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA:0:11}
|
||||
DESTINATIONS="$DESTINATIONS --destination=$COMMIT_SHORT_SHA_DESTINATION"
|
||||
fi
|
||||
|
||||
# Also tag the image with GitLab version, if running on a tag pipeline, so
|
||||
# other projects can simply use that instead of computing the slug.
|
||||
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||
COMMIT_REF_NAME_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
|
||||
DESTINATIONS="$DESTINATIONS --destination=$COMMIT_REF_NAME_DESTINATION"
|
||||
fi
|
||||
|
||||
|
|
|
@ -204,6 +204,32 @@ RSpec.describe "Admin::Users" do
|
|||
expect(page).to have_content(user.email)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when blocking a user' do
|
||||
it 'shows confirmation and allows blocking', :js do
|
||||
expect(page).to have_content(user.email)
|
||||
|
||||
find("[data-testid='user-action-button-#{user.id}']").click
|
||||
|
||||
within find("[data-testid='user-action-dropdown-#{user.id}']") do
|
||||
find('li button', text: 'Block').click
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Block user')
|
||||
expect(page).to have_content('Blocking user has the following effects')
|
||||
expect(page).to have_content('User will not be able to login')
|
||||
expect(page).to have_content('Owned groups will be left')
|
||||
|
||||
find('.modal-footer button', text: 'Block').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Successfully blocked')
|
||||
expect(page).not_to have_content(user.email)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /admin/users/new" do
|
||||
|
@ -362,6 +388,26 @@ RSpec.describe "Admin::Users" do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when blocking the user' do
|
||||
it 'shows confirmation and allows blocking', :js do
|
||||
visit admin_user_path(user)
|
||||
|
||||
find('button', text: 'Block user').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Block user')
|
||||
expect(page).to have_content('You can always unblock their account, their data will remain intact.')
|
||||
|
||||
find('.modal-footer button', text: 'Block').click
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Successfully blocked')
|
||||
expect(page).to have_content('This user is blocked')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Impersonation' do
|
||||
let(:another_user) { create(:user) }
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLoadingIcon, GlButton } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlButton, GlAlert } from '@gitlab/ui';
|
||||
import { TEST_HOST } from 'spec/test_constants';
|
||||
import TerminalEmptyState from '~/ide/components/terminal/empty_state.vue';
|
||||
|
||||
|
@ -101,6 +101,6 @@ describe('IDE TerminalEmptyState', () => {
|
|||
});
|
||||
|
||||
expect(wrapper.find(GlButton).props('disabled')).toBe(true);
|
||||
expect(wrapper.find('.bs-callout').element.innerHTML).toBe(TEST_HTML_MESSAGE);
|
||||
expect(wrapper.find(GlAlert).html()).toContain(TEST_HTML_MESSAGE);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -159,13 +159,13 @@ describe('graph component', () => {
|
|||
|
||||
describe('triggered by', () => {
|
||||
describe('on click', () => {
|
||||
it('should emit `onClickTriggeredBy` when triggered by linked pipeline is clicked', () => {
|
||||
it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => {
|
||||
const btnWrapper = findExpandPipelineBtn();
|
||||
|
||||
btnWrapper.trigger('click');
|
||||
|
||||
btnWrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.emitted().onClickTriggeredBy).toEqual([
|
||||
expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([
|
||||
store.state.pipeline.triggered_by,
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -2,11 +2,10 @@ import { mount } from '@vue/test-utils';
|
|||
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
|
||||
import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue';
|
||||
import CiStatus from '~/vue_shared/components/ci_icon.vue';
|
||||
|
||||
import mockData from './linked_pipelines_mock_data';
|
||||
import { UPSTREAM, DOWNSTREAM } from '~/pipelines/components/graph/constants';
|
||||
|
||||
const mockPipeline = mockData.triggered[0];
|
||||
|
||||
const validTriggeredPipelineId = mockPipeline.project.id;
|
||||
const invalidTriggeredPipelineId = mockPipeline.project.id + 5;
|
||||
|
||||
|
@ -40,6 +39,7 @@ describe('Linked pipeline', () => {
|
|||
pipeline: mockPipeline,
|
||||
projectId: invalidTriggeredPipelineId,
|
||||
columnTitle: 'Downstream',
|
||||
type: DOWNSTREAM,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -104,11 +104,13 @@ describe('Linked pipeline', () => {
|
|||
pipeline: mockPipeline,
|
||||
projectId: validTriggeredPipelineId,
|
||||
columnTitle: 'Downstream',
|
||||
type: DOWNSTREAM,
|
||||
};
|
||||
|
||||
const upstreamProps = {
|
||||
...downstreamProps,
|
||||
columnTitle: 'Upstream',
|
||||
type: UPSTREAM,
|
||||
};
|
||||
|
||||
it('parent/child label container should exist', () => {
|
||||
|
@ -182,6 +184,7 @@ describe('Linked pipeline', () => {
|
|||
pipeline: { ...mockPipeline, isLoading: true },
|
||||
projectId: invalidTriggeredPipelineId,
|
||||
columnTitle: 'Downstream',
|
||||
type: DOWNSTREAM,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -198,6 +201,7 @@ describe('Linked pipeline', () => {
|
|||
pipeline: mockPipeline,
|
||||
projectId: validTriggeredPipelineId,
|
||||
columnTitle: 'Downstream',
|
||||
type: DOWNSTREAM,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
|
||||
import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
|
||||
import { UPSTREAM } from '~/pipelines/components/graph/constants';
|
||||
import mockData from './linked_pipelines_mock_data';
|
||||
|
||||
describe('Linked Pipelines Column', () => {
|
||||
|
@ -9,6 +10,7 @@ describe('Linked Pipelines Column', () => {
|
|||
linkedPipelines: mockData.triggered,
|
||||
graphPosition: 'right',
|
||||
projectId: 19,
|
||||
type: UPSTREAM,
|
||||
};
|
||||
let wrapper;
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ describe('PrometheusMetrics', () => {
|
|||
expect(prometheusMetrics.$monitoredMetricsEmpty).toBeDefined();
|
||||
expect(prometheusMetrics.$monitoredMetricsList).toBeDefined();
|
||||
expect(prometheusMetrics.$missingEnvVarPanel).toBeDefined();
|
||||
expect(prometheusMetrics.$panelToggle).toBeDefined();
|
||||
expect(prometheusMetrics.$panelToggleRight).toBeDefined();
|
||||
expect(prometheusMetrics.$panelToggleDown).toBeDefined();
|
||||
expect(prometheusMetrics.$missingEnvVarMetricCount).toBeDefined();
|
||||
expect(prometheusMetrics.$missingEnvVarMetricsList).toBeDefined();
|
||||
});
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('vue_shared/components/confirm_modal', () => {
|
|||
wrapper.vm.modalAttributes = MOCK_MODAL_DATA.modalAttributes;
|
||||
});
|
||||
|
||||
it('renders GlModal wtih data', () => {
|
||||
it('renders GlModal with data', () => {
|
||||
expect(findModal().exists()).toBeTruthy();
|
||||
expect(findModal().attributes()).toEqual(
|
||||
expect.objectContaining({
|
||||
|
@ -72,6 +72,24 @@ describe('vue_shared/components/confirm_modal', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
desc | attrs | expectation
|
||||
${'when message is simple text'} | ${{}} | ${`<div>${MOCK_MODAL_DATA.modalAttributes.message}</div>`}
|
||||
${'when message has html'} | ${{ messageHtml: '<p>Header</p><ul onhover="alert(1)"><li>First</li></ul>' }} | ${'<p>Header</p><ul><li>First</li></ul>'}
|
||||
`('$desc', ({ attrs, expectation }) => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
wrapper.vm.modalAttributes = {
|
||||
...MOCK_MODAL_DATA.modalAttributes,
|
||||
...attrs,
|
||||
};
|
||||
});
|
||||
|
||||
it('renders message', () => {
|
||||
expect(findForm().element.innerHTML).toContain(expectation);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
|
|
|
@ -400,13 +400,13 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
|
|||
allow(described_class).to receive(:aggregated_metrics).and_return(aggregated_metrics)
|
||||
end
|
||||
|
||||
context 'with ALL operator' do
|
||||
context 'with AND operator' do
|
||||
let(:aggregated_metrics) do
|
||||
[
|
||||
{ name: 'gmau_1', events: %w[event1_slot event2_slot], operator: "ALL" },
|
||||
{ name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot], operator: "ALL" },
|
||||
{ name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "ALL" },
|
||||
{ name: 'gmau_4', events: %w[event4], operator: "ALL" }
|
||||
{ name: 'gmau_1', events: %w[event1_slot event2_slot], operator: "AND" },
|
||||
{ name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot], operator: "AND" },
|
||||
{ name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "AND" },
|
||||
{ name: 'gmau_4', events: %w[event4], operator: "AND" }
|
||||
].map(&:with_indifferent_access)
|
||||
end
|
||||
|
||||
|
@ -422,12 +422,12 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
|
|||
end
|
||||
end
|
||||
|
||||
context 'with ANY operator' do
|
||||
context 'with OR operator' do
|
||||
let(:aggregated_metrics) do
|
||||
[
|
||||
{ name: 'gmau_1', events: %w[event3_slot event5_slot], operator: "ANY" },
|
||||
{ name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "ANY" },
|
||||
{ name: 'gmau_3', events: %w[event4], operator: "ANY" }
|
||||
{ name: 'gmau_1', events: %w[event3_slot event5_slot], operator: "OR" },
|
||||
{ name: 'gmau_2', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "OR" },
|
||||
{ name: 'gmau_3', events: %w[event4], operator: "OR" }
|
||||
].map(&:with_indifferent_access)
|
||||
end
|
||||
|
||||
|
@ -448,11 +448,11 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
|
|||
let(:aggregated_metrics) do
|
||||
[
|
||||
# represents stable aggregated metrics that has been fully released
|
||||
{ name: 'gmau_without_ff', events: %w[event3_slot event5_slot], operator: "ANY" },
|
||||
{ name: 'gmau_without_ff', events: %w[event3_slot event5_slot], operator: "OR" },
|
||||
# represents new aggregated metric that is under performance testing on gitlab.com
|
||||
{ name: 'gmau_enabled', events: %w[event4], operator: "ALL", feature_flag: enabled_feature_flag },
|
||||
{ name: 'gmau_enabled', events: %w[event4], operator: "AND", feature_flag: enabled_feature_flag },
|
||||
# represents aggregated metric that is under development and shouldn't be yet collected even on gitlab.com
|
||||
{ name: 'gmau_disabled', events: %w[event4], operator: "ALL", feature_flag: disabled_feature_flag }
|
||||
{ name: 'gmau_disabled', events: %w[event4], operator: "AND", feature_flag: disabled_feature_flag }
|
||||
].map(&:with_indifferent_access)
|
||||
end
|
||||
|
||||
|
@ -520,7 +520,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
|
|||
context 'Redis calls' do
|
||||
let(:aggregated_metrics) do
|
||||
[
|
||||
{ name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "ALL" }
|
||||
{ name: 'gmau_3', events: %w[event1_slot event2_slot event3_slot event5_slot], operator: "AND" }
|
||||
].map(&:with_indifferent_access)
|
||||
end
|
||||
|
||||
|
|
|
@ -26,18 +26,6 @@ RSpec.describe Ci::BuildReportResultService do
|
|||
expect(unique_test_cases_parsed).to eq(4)
|
||||
end
|
||||
|
||||
context 'when feature flag for tracking is disabled' do
|
||||
before do
|
||||
stub_feature_flags(track_unique_test_cases_parsed: false)
|
||||
end
|
||||
|
||||
it 'creates the report but does not track the event' do
|
||||
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
|
||||
expect(build_report_result.tests_name).to eq("test")
|
||||
expect(Ci::BuildReportResult.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when data has already been persisted' do
|
||||
it 'raises an error and do not persist the same data twice' do
|
||||
expect { 2.times { described_class.new.execute(build) } }.to raise_error(ActiveRecord::RecordNotUnique)
|
||||
|
|
|
@ -147,9 +147,11 @@ RSpec.describe Snippets::CreateService do
|
|||
end
|
||||
|
||||
context 'when the commit action fails' do
|
||||
let(:error) { SnippetRepository::CommitError.new('foobar') }
|
||||
|
||||
before do
|
||||
allow_next_instance_of(SnippetRepository) do |instance|
|
||||
allow(instance).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError.new('foobar'))
|
||||
allow(instance).to receive(:multi_files_action).and_raise(error)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -172,7 +174,7 @@ RSpec.describe Snippets::CreateService do
|
|||
end
|
||||
|
||||
it 'logs the error' do
|
||||
expect(Gitlab::AppLogger).to receive(:error).with('foobar')
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(error, service: 'Snippets::CreateService')
|
||||
|
||||
subject
|
||||
end
|
||||
|
|
|
@ -277,14 +277,14 @@ RSpec.describe Snippets::UpdateService do
|
|||
end
|
||||
|
||||
context 'when an error is raised' do
|
||||
let(:error_message) { 'foobar' }
|
||||
let(:error) { SnippetRepository::CommitError.new('foobar') }
|
||||
|
||||
before do
|
||||
allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError, error_message)
|
||||
allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(error)
|
||||
end
|
||||
|
||||
it 'logs the error' do
|
||||
expect(Gitlab::AppLogger).to receive(:error).with(error_message)
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(error, service: 'Snippets::UpdateService')
|
||||
|
||||
subject
|
||||
end
|
||||
|
|
|
@ -124,9 +124,6 @@ RSpec.shared_examples 'write access for a read-only GitLab instance' do
|
|||
|
||||
where(:description, :path) do
|
||||
'LFS request to batch' | '/root/rouge.git/info/lfs/objects/batch'
|
||||
'LFS request to locks verify' | '/root/rouge.git/info/lfs/locks/verify'
|
||||
'LFS request to locks create' | '/root/rouge.git/info/lfs/locks'
|
||||
'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock'
|
||||
'request to git-upload-pack' | '/root/rouge.git/git-upload-pack'
|
||||
end
|
||||
|
||||
|
@ -139,6 +136,21 @@ RSpec.shared_examples 'write access for a read-only GitLab instance' do
|
|||
expect(subject).not_to disallow_request
|
||||
end
|
||||
end
|
||||
|
||||
where(:description, :path) do
|
||||
'LFS request to locks verify' | '/root/rouge.git/info/lfs/locks/verify'
|
||||
'LFS request to locks create' | '/root/rouge.git/info/lfs/locks'
|
||||
'LFS request to locks unlock' | '/root/rouge.git/info/lfs/locks/1/unlock'
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "expects a POST #{description} URL not to be allowed" do
|
||||
response = request.post(path)
|
||||
|
||||
expect(response).to be_redirect
|
||||
expect(subject).to disallow_request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue