Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3eeaac66d3
commit
8bee002871
|
|
@ -73,19 +73,6 @@ Layout/ArgumentAlignment:
|
|||
- 'ee/spec/requests/api/deployments_spec.rb'
|
||||
- 'ee/spec/requests/api/dora/metrics_spec.rb'
|
||||
- 'ee/spec/requests/api/epics_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/security_policy/assign_security_policy_project_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/security_policy/create_security_policy_project_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/security_policy/unassign_security_policy_project_spec.rb'
|
||||
- 'ee/spec/requests/api/group_boards_spec.rb'
|
||||
- 'ee/spec/requests/api/group_clusters_spec.rb'
|
||||
- 'ee/spec/requests/api/group_push_rule_spec.rb'
|
||||
- 'ee/spec/requests/api/groups_spec.rb'
|
||||
- 'ee/spec/requests/api/internal/base_spec.rb'
|
||||
- 'ee/spec/requests/api/invitations_spec.rb'
|
||||
- 'ee/spec/requests/api/issue_links_spec.rb'
|
||||
- 'ee/spec/requests/api/issues_spec.rb'
|
||||
- 'ee/spec/requests/api/members_spec.rb'
|
||||
- 'ee/spec/requests/api/merge_trains_spec.rb'
|
||||
- 'ee/spec/requests/api/namespaces_spec.rb'
|
||||
- 'ee/spec/requests/api/project_clusters_spec.rb'
|
||||
- 'ee/spec/requests/api/project_push_rule_spec.rb'
|
||||
|
|
|
|||
|
|
@ -62,21 +62,8 @@ Lint/SymbolConversion:
|
|||
- 'ee/spec/models/concerns/elastic/repository_spec.rb'
|
||||
- 'ee/spec/models/plan_spec.rb'
|
||||
- 'ee/spec/models/project_feature_spec.rb'
|
||||
- 'ee/spec/requests/api/analytics/product_analytics_spec.rb'
|
||||
- 'ee/spec/requests/api/dependency_proxy/packages/npm_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/create_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/destroy_spec.rb'
|
||||
- 'ee/spec/requests/api/graphql/mutations/audit_events/external_audit_event_destinations/update_spec.rb'
|
||||
- 'ee/spec/requests/api/scim/group_scim_spec.rb'
|
||||
- 'ee/spec/requests/api/scim/instance_scim_spec.rb'
|
||||
- 'ee/spec/services/elastic/data_migration_service_spec.rb'
|
||||
- 'ee/spec/services/phone_verification/users/send_verification_code_service_spec.rb'
|
||||
- 'ee/spec/services/security/security_orchestration_policies/scan_pipeline_service_spec.rb'
|
||||
- 'ee/spec/services/security/token_revocation_service_spec.rb'
|
||||
- 'ee/spec/support/helpers/subscription_portal_helpers.rb'
|
||||
- 'ee/spec/support/matchers/ee/epic_aggregate_matchers.rb'
|
||||
- 'ee/spec/support/shared_examples/google_cloud_platform/artifact_registry/services_shared_examples.rb'
|
||||
- 'ee/spec/support/shared_examples/status_page/image_post_process_examples.rb'
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export default {
|
|||
:items="messages"
|
||||
:fields="$options.fields"
|
||||
:tbody-tr-attr="{ 'data-testid': 'message-row' }"
|
||||
class="-gl-mt-1 gl-mb-n2"
|
||||
class="-gl-mt-1 -gl-mb-2"
|
||||
stacked="md"
|
||||
>
|
||||
<template #cell(preview)="{ item: { message, theme, broadcast_type, dismissable } }">
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ export default {
|
|||
:fields="$options.fields"
|
||||
stacked="md"
|
||||
data-testid="deploy-keys-list"
|
||||
class="-gl-mt-1 gl-mb-n2"
|
||||
class="-gl-mt-1 -gl-mb-2"
|
||||
>
|
||||
<template #table-busy>
|
||||
<gl-loading-icon size="sm" class="gl-my-5" />
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export default {
|
|||
</template>
|
||||
|
||||
<template #cell(actions)="{ item }">
|
||||
<gl-button-group class="gl-ml-3 -gl-mt-2 gl-mb-n2">
|
||||
<gl-button-group class="gl-ml-3 -gl-mt-2 -gl-mb-2">
|
||||
<gl-button
|
||||
icon="settings"
|
||||
:aria-label="$options.i18n.editIntegration"
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ export default {
|
|||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="board-card-assignee gl-display-flex gl-mb-n2">
|
||||
<div class="board-card-assignee gl-display-flex -gl-mb-2">
|
||||
<user-avatar-link
|
||||
v-for="assignee in cappedAssignees"
|
||||
:key="assignee.id"
|
||||
|
|
@ -368,7 +368,7 @@ export default {
|
|||
v-if="shouldRenderCounter"
|
||||
v-gl-tooltip
|
||||
:title="assigneeCounterTooltip"
|
||||
class="avatar-counter gl-bg-gray-100 gl-text-gray-900 gl-cursor-help gl-font-weight-bold gl-border-0 gl-leading-24 gl-ml-n3"
|
||||
class="avatar-counter gl-bg-gray-100 gl-text-gray-900 gl-cursor-help gl-font-weight-bold gl-border-0 gl-leading-24 -gl-ml-3"
|
||||
data-placement="bottom"
|
||||
>{{ assigneeCounterLabel }}</span
|
||||
>
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ export default {
|
|||
</gl-form-group>
|
||||
<gl-form-group class="gl-border-none -gl-mb-3">
|
||||
<template #label>
|
||||
<div class="gl-mb-n3">
|
||||
<div class="-gl-mb-3">
|
||||
{{ $options.i18n.visibility }}
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export default {
|
|||
hide-tooltip
|
||||
/>
|
||||
|
||||
<div class="gl-font-weight-100 gl-font-size-lg -gl-ml-4 gl-align-self-center">
|
||||
<div class="gl-font-size-lg -gl-ml-4 gl-align-self-center">
|
||||
{{ group.size }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ export default {
|
|||
v-if="node.attrs.showPreview"
|
||||
:contenteditable="false"
|
||||
data-testid="sandbox-preview"
|
||||
class="!-gl-mt-3 gl-ml-n4! !-gl-mr-4 gl-mb-3 gl-bg-white! gl-p-4 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
|
||||
class="!-gl-mt-3 !-gl-ml-4 !-gl-mr-4 gl-mb-3 gl-bg-white! gl-p-4 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
|
||||
>
|
||||
<sandboxed-mermaid v-if="node.attrs.language === 'mermaid'" :source="diagramSource" />
|
||||
<img v-else ref="diagramContainer" :src="diagramUrl" />
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ export default {
|
|||
{{ item.cronTimezone.formattedTimezone }}
|
||||
</template>
|
||||
<template #cell(actions)="{ item }">
|
||||
<div class="gl-display-flex gl-justify-content-end -gl-mt-2 gl-mb-n2">
|
||||
<div class="gl-display-flex gl-justify-content-end -gl-mt-2 -gl-mb-2">
|
||||
<gl-button
|
||||
v-gl-modal.deploy-freeze-modal
|
||||
icon="pencil"
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ export default {
|
|||
<template v-if="discussion.resolvable" #resolve-checkbox>
|
||||
<gl-form-checkbox
|
||||
v-model="shouldChangeResolvedStatus"
|
||||
class="gl-mt-5 gl-mb-n3"
|
||||
class="gl-mt-5 -gl-mb-3"
|
||||
data-testid="resolve-checkbox"
|
||||
>
|
||||
{{ resolveCheckboxText }}
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ export default {
|
|||
v-if="isReviewable && showLocalFileReviews"
|
||||
v-gl-tooltip.hover.focus.left
|
||||
data-testid="fileReviewCheckbox"
|
||||
class="gl-mr-5 gl-mb-n3 gl-display-flex gl-align-items-center"
|
||||
class="gl-mr-5 -gl-mb-3 gl-display-flex gl-align-items-center"
|
||||
:title="$options.i18n.fileReviewTooltip"
|
||||
:checked="reviewed"
|
||||
@change="toggleReview"
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button-group class="gl-flex-direction-column gl-md-flex-direction-row gl-ml-n6">
|
||||
<gl-button-group class="gl-flex-direction-column gl-md-flex-direction-row -gl-ml-6">
|
||||
<gl-button
|
||||
:key="ignoreBtn.status"
|
||||
:ref="`${ignoreBtn.title.toLowerCase()}Error`"
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
<h3 class="gl-new-card-title">{{ $options.i18n.activeIntegrationsHeading }}</h3>
|
||||
</template>
|
||||
<integrations-table
|
||||
class="gl-mb-n2"
|
||||
class="-gl-mb-2"
|
||||
:integrations="integrationsGrouped.active"
|
||||
:empty-text="$options.i18n.activeTableEmptyText"
|
||||
show-updated-at
|
||||
|
|
@ -67,7 +67,7 @@ export default {
|
|||
<h3 class="gl-new-card-title">{{ $options.i18n.inactiveIntegrationsHeading }}</h3>
|
||||
</template>
|
||||
<integrations-table
|
||||
class="gl-mb-n2"
|
||||
class="-gl-mb-2"
|
||||
inactive
|
||||
:integrations="integrationsGrouped.inactive"
|
||||
:empty-text="$options.i18n.inactiveTableEmptyText"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
|
|||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import EmptyState from '../components/empty_state.vue';
|
||||
import EmptyState from '../components/model_list_empty_state.vue';
|
||||
import * as i18n from '../translations';
|
||||
import { BASE_SORT_FIELDS, MODEL_ENTITIES } from '../constants';
|
||||
import ModelRow from '../components/model_row.vue';
|
||||
|
|
@ -71,6 +71,7 @@ export default {
|
|||
errorMessage: '',
|
||||
skipQueries: true,
|
||||
queryVariables: {},
|
||||
createModelVisible: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -127,7 +128,12 @@ export default {
|
|||
<metadata-item icon="machine-learning" :text="$options.i18n.modelsCountLabel(count)" />
|
||||
</template>
|
||||
<template #right-actions>
|
||||
<model-create v-if="canWriteModelRegistry" />
|
||||
<model-create
|
||||
v-if="canWriteModelRegistry"
|
||||
:create-model-visible="createModelVisible"
|
||||
@show-create-model="createModelVisible = true"
|
||||
@hide-create-model="createModelVisible = false"
|
||||
/>
|
||||
|
||||
<actions-dropdown />
|
||||
</template>
|
||||
|
|
@ -142,7 +148,7 @@ export default {
|
|||
@fetch-page="fetchPage"
|
||||
>
|
||||
<template #empty-state>
|
||||
<empty-state :entity-type="$options.modelEntity" />
|
||||
<empty-state @open-create-model="createModelVisible = true" />
|
||||
</template>
|
||||
|
||||
<template #item="{ item }">
|
||||
|
|
|
|||
|
|
@ -28,13 +28,18 @@ export default {
|
|||
ImportArtifactZone: () => import('./import_artifact_zone.vue'),
|
||||
},
|
||||
inject: ['projectPath'],
|
||||
props: {
|
||||
createModelVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: null,
|
||||
version: null,
|
||||
description: null,
|
||||
versionDescription: null,
|
||||
modalVisible: false,
|
||||
errorMessage: null,
|
||||
selectedFile: null,
|
||||
modelData: null,
|
||||
|
|
@ -107,10 +112,10 @@ export default {
|
|||
}
|
||||
},
|
||||
showCreateModal() {
|
||||
this.modalVisible = true;
|
||||
this.$emit('show-create-model');
|
||||
},
|
||||
cancelModal() {
|
||||
this.modalVisible = false;
|
||||
this.$emit('hide-create-model');
|
||||
},
|
||||
hideAlert() {
|
||||
this.errorMessage = null;
|
||||
|
|
@ -158,13 +163,14 @@ export default {
|
|||
<div>
|
||||
<gl-button @click="showCreateModal">{{ $options.modal.buttonTitle }}</gl-button>
|
||||
<gl-modal
|
||||
v-model="modalVisible"
|
||||
modal-id="create-model-modal"
|
||||
:visible="createModelVisible"
|
||||
:title="$options.modal.title"
|
||||
:action-primary="$options.modal.actionPrimary"
|
||||
:action-cancel="$options.modal.actionCancel"
|
||||
size="sm"
|
||||
@primary="create"
|
||||
@hide="cancelModal"
|
||||
@cancel="cancelModal"
|
||||
>
|
||||
<gl-form>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
<script>
|
||||
import { GlEmptyState, GlButton } from '@gitlab/ui';
|
||||
import emptySvgUrl from '@gitlab/svgs/dist/illustrations/empty-state/empty-dag-md.svg?url';
|
||||
import { helpPagePath } from '~/helpers/help_page_helper';
|
||||
import { s__ } from '~/locale';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlEmptyState,
|
||||
ClipboardButton,
|
||||
GlButton,
|
||||
},
|
||||
inject: ['mlflowTrackingUrl'],
|
||||
title: s__('MlModelRegistry|No models registered'),
|
||||
description: s__(
|
||||
'MlModelRegistry|Import your machine learning using GitLab directly or using the MLflow client:',
|
||||
),
|
||||
createNew: s__('MlModelRegistry|Create model'),
|
||||
mlflowDocs: s__('MlModelRegistry|MLflow compatibility'),
|
||||
helpPath: helpPagePath('user/project/ml/model_registry/index', {
|
||||
anchor: 'creating-machine-learning-models-and-model-versions',
|
||||
}),
|
||||
emptySvgPath: emptySvgUrl,
|
||||
computed: {
|
||||
mlflowCommand() {
|
||||
return [
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
'import os',
|
||||
'from mlflow import MlflowClient',
|
||||
'',
|
||||
`os.environ["MLFLOW_TRACKING_URI"] = "${this.mlflowTrackingUrl}"`,
|
||||
'os.environ["MLFLOW_TRACKING_TOKEN"] = <your_gitlab_token>',
|
||||
'',
|
||||
s__('MlModelRegistry|# Create a model'),
|
||||
'client = MlflowClient()',
|
||||
"model_name = '<your_model_name>'",
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
"description = 'Model description'",
|
||||
'model = client.create_registered_model(model_name, description=description)',
|
||||
'',
|
||||
s__('MlModelRegistry|# Create a version'),
|
||||
'tags = { "gitlab.version": version }',
|
||||
'model_version = client.create_model_version(model_name, version, tags=tags)',
|
||||
'',
|
||||
s__('MlModelRegistry|# Log artifacts'),
|
||||
'client.log_artifact(run_id, \'<local/path/to/file.txt>\', artifact_path="")',
|
||||
].join('\n');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
emitOpenCreateModel() {
|
||||
this.$emit('open-create-model');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-empty-state
|
||||
:title="$options.title"
|
||||
:svg-path="$options.emptySvgPath"
|
||||
:svg-height="null"
|
||||
class="gl-py-8"
|
||||
>
|
||||
<template #description>
|
||||
<p>{{ $options.description }}</p>
|
||||
<pre
|
||||
class="code highlight gl-flex gl-border-none gl-text-left gl-p-2"
|
||||
data-testid="preview-code"
|
||||
>
|
||||
<code>{{ mlflowCommand }}</code>
|
||||
<clipboard-button
|
||||
category="tertiary"
|
||||
:text="mlflowCommand"
|
||||
class="gl-self-start"
|
||||
:title="__('Copy')"
|
||||
/>
|
||||
</pre>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<gl-button variant="confirm" class="gl-mx-2 gl-mb-3" @click="emitOpenCreateModel">{{
|
||||
$options.createNew
|
||||
}}</gl-button>
|
||||
<gl-button class="gl-mb-3 gl-mr-3 gl-mx-2" :href="$options.helpPath"
|
||||
>{{ $options.mlflowDocs }}
|
||||
</gl-button>
|
||||
</template>
|
||||
</gl-empty-state>
|
||||
</template>
|
||||
|
|
@ -232,7 +232,7 @@ export default {
|
|||
:aria-label="$options.i18n.SEARCH_OR_COMMAND_MODE_PLACEHOLDER"
|
||||
class="gl-relative gl-rounded-lg gl-w-full gl-pb-0"
|
||||
>
|
||||
<div class="input-box-wrapper gl-bg-white gl-border-b gl-mb-n1 gl-p-2">
|
||||
<div class="input-box-wrapper gl-bg-white gl-border-b -gl-mb-1 gl-p-2">
|
||||
<gl-search-box-by-type
|
||||
id="search"
|
||||
ref="searchInput"
|
||||
|
|
|
|||
|
|
@ -2,20 +2,36 @@
|
|||
import { GlDisclosureDropdownGroup } from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { InternalEvents } from '~/tracking';
|
||||
import { ALL_GITLAB } from '~/vue_shared/global_search/constants';
|
||||
import { OVERLAY_GOTO } from '../command_palette/constants';
|
||||
import {
|
||||
OVERLAY_GOTO,
|
||||
ISSUES_ASSIGNED_TO_ME_TITLE,
|
||||
ISSUES_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_THAT_I_AM_A_REVIEWER,
|
||||
MERGE_REQUESTS_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_ASSIGNED_TO_ME_TITLE,
|
||||
} from '~/super_sidebar/components/global_search/command_palette/constants';
|
||||
import SearchResultHoverLayover from './global_search_hover_overlay.vue';
|
||||
|
||||
const trackingMixin = InternalEvents.mixin();
|
||||
|
||||
export default {
|
||||
name: 'DefaultIssuables',
|
||||
i18n: {
|
||||
ALL_GITLAB,
|
||||
OVERLAY_GOTO,
|
||||
ISSUES_ASSIGNED_TO_ME_TITLE,
|
||||
ISSUES_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_THAT_I_AM_A_REVIEWER,
|
||||
MERGE_REQUESTS_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_ASSIGNED_TO_ME_TITLE,
|
||||
},
|
||||
components: {
|
||||
GlDisclosureDropdownGroup,
|
||||
SearchResultHoverLayover,
|
||||
},
|
||||
mixins: [trackingMixin],
|
||||
computed: {
|
||||
...mapState(['searchContext']),
|
||||
...mapGetters(['defaultSearchOptions']),
|
||||
|
|
@ -46,11 +62,46 @@ export default {
|
|||
this.$emit('nothing-to-render');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
trackingTypes({ text }) {
|
||||
switch (text) {
|
||||
case this.$options.i18n.ISSUES_ASSIGNED_TO_ME_TITLE: {
|
||||
this.trackEvent('click_issues_assigned_to_me_in_command_palette');
|
||||
break;
|
||||
}
|
||||
case this.$options.i18n.ISSUES_I_HAVE_CREATED_TITLE: {
|
||||
this.trackEvent('click_issues_i_created_in_command_palette');
|
||||
break;
|
||||
}
|
||||
case this.$options.i18n.MERGE_REQUESTS_ASSIGNED_TO_ME_TITLE: {
|
||||
this.trackEvent('click_merge_requests_assigned_to_me_in_command_palette');
|
||||
break;
|
||||
}
|
||||
case this.$options.i18n.MERGE_REQUESTS_THAT_I_AM_A_REVIEWER: {
|
||||
this.trackEvent('click_merge_requests_that_im_a_reviewer_in_command_palette');
|
||||
break;
|
||||
}
|
||||
case this.$options.i18n.MERGE_REQUESTS_I_HAVE_CREATED_TITLE: {
|
||||
this.trackEvent('click_merge_requests_i_created_in_command_palette');
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-disclosure-dropdown-group v-if="shouldRender" v-bind="$attrs" :group="group">
|
||||
<gl-disclosure-dropdown-group
|
||||
v-if="shouldRender"
|
||||
v-bind="$attrs"
|
||||
:group="group"
|
||||
@action="trackingTypes"
|
||||
>
|
||||
<template #list-item="{ item }">
|
||||
<search-result-hover-layover :text-message="$options.i18n.OVERLAY_GOTO">
|
||||
<span>{{ item.text }}</span>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ module Groups
|
|||
@children = GroupDescendantsFinder.new(
|
||||
current_user: current_user,
|
||||
parent_group: parent,
|
||||
params: group_descendants_params
|
||||
params: safe_params
|
||||
).execute.page(params[:page])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -58,17 +58,9 @@ module AutoMerge
|
|||
|
||||
def available_for?(merge_request)
|
||||
strong_memoize("available_for_#{merge_request.id}") do
|
||||
if Feature.enabled?(:refactor_auto_merge, merge_request.project, type: :gitlab_com_derisk)
|
||||
merge_request.can_be_merged_by?(current_user) &&
|
||||
merge_request.mergeability_checks_pass?(**skippable_available_for_checks(merge_request)) &&
|
||||
yield
|
||||
else
|
||||
merge_request.can_be_merged_by?(current_user) &&
|
||||
merge_request.open? &&
|
||||
!merge_request.broken? &&
|
||||
overrideable_available_for_checks(merge_request) &&
|
||||
yield
|
||||
end
|
||||
merge_request.can_be_merged_by?(current_user) &&
|
||||
merge_request.mergeability_checks_pass?(**skippable_available_for_checks(merge_request)) &&
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -81,12 +73,6 @@ module AutoMerge
|
|||
)
|
||||
end
|
||||
|
||||
def overrideable_available_for_checks(merge_request)
|
||||
!merge_request.draft? &&
|
||||
merge_request.mergeable_discussions_state? &&
|
||||
!merge_request.merge_blocked_by_other_mrs?
|
||||
end
|
||||
|
||||
# Overridden in child classes
|
||||
def notify(merge_request)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
%div{ class: 'top-bar-fixed container-fluid', data: { testid: 'top-bar' } }
|
||||
.top-bar-container.gl-display-flex.gl-align-items-center.gl-gap-2
|
||||
= render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle-expand super-sidebar-toggle gl-ml-n3', aria: { controls: 'super-sidebar', expanded: 'false', label: _('Primary navigation sidebar') } })
|
||||
= render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle-expand super-sidebar-toggle -gl-ml-3', aria: { controls: 'super-sidebar', expanded: 'false', label: _('Primary navigation sidebar') } })
|
||||
= render "layouts/nav/breadcrumbs/breadcrumbs"
|
||||
= render_if_exists "layouts/nav/ask_duo_button"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch
|
||||
|
||||
.gl-text-truncate.-gl-my-2.gl-ml-n2
|
||||
.gl-text-truncate.-gl-my-2.-gl-ml-2
|
||||
- if commit
|
||||
= render 'projects/branches/commit', commit: commit, project: @project, class_name: 'gl-p-2'
|
||||
- else
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
.item-contents.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-flex-grow-1.gl-min-h-7
|
||||
.item-title.gl-display-flex.mb-xl-0.gl-min-w-0.gl-align-items-center
|
||||
- if branch[:pipeline_status].present?
|
||||
%span.-gl-mt-2.gl-mb-n2.gl-mr-3
|
||||
%span.-gl-mt-2.-gl-mb-2.gl-mr-3
|
||||
= render 'ci/status/icon', status: branch[:pipeline_status]
|
||||
%span.related-branch-info
|
||||
%strong
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
= form_tag network_path, method: :get, class: 'form-inline network-form' do |f|
|
||||
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control gl-form-input input-mx-250 search-sha gl-mr-2'
|
||||
= render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, icon: 'search')
|
||||
.form-group{ class: 'gl-ml-5 gl-mb-n3!' }
|
||||
.form-group{ class: 'gl-ml-5 !gl-mb-3' }
|
||||
= render Pajamas::CheckboxTagComponent.new(name: :filter_ref, checked: @options[:filter_ref]) do |c|
|
||||
- c.with_label do
|
||||
= _("Begin with the selected commit")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
%li
|
||||
= clipboard_button(text: noteable_note_url(note), title: _('Copy reference'), button_text: _('Copy link'), class: 'gl-rounded-0!', size: :medium, hide_tooltip: true, hide_button_icon: true)
|
||||
- unless is_current_user
|
||||
.gl-ml-n2
|
||||
.-gl-ml-2
|
||||
.js-report-abuse-dropdown-item{ data: { report_abuse_path: add_category_abuse_reports_path, reported_user_id: note.author.id, reported_from_url: noteable_note_url(note) } }
|
||||
- if note_editable
|
||||
%li
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
- if @applications.any?
|
||||
.table-holder
|
||||
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.gl-mb-n2
|
||||
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.-gl-mb-2
|
||||
%thead.gl-hidden.md:gl-table-header-group
|
||||
%tr
|
||||
%th= _('Name')
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
- c.with_body do
|
||||
- if @authorized_tokens.any?
|
||||
.table-holder
|
||||
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.gl-mb-n2
|
||||
%table.table.b-table.gl-table.b-table-stacked-sm.-gl-mt-1.-gl-mb-2
|
||||
%thead.gl-hidden.md:gl-table-header-group
|
||||
%tr
|
||||
%th= _('Name')
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
= markdown_field(project, :description)
|
||||
|
||||
- if project.topics.any?
|
||||
.gl-mt-3.gl-ml-n1
|
||||
.gl-mt-3.-gl-ml-1
|
||||
= render "shared/projects/topics", project: project.present(current_user: current_user)
|
||||
- if project.catalog_resource
|
||||
= render partial: 'shared/ci_catalog_badge', locals: { href: explore_catalog_path(project.catalog_resource), css_class: 'gl-mt-2' }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
- require_external_verification = Integrations::BeyondIdentity.activated_for_instance?
|
||||
- if @gpg_keys.any?
|
||||
.table-holder
|
||||
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.gl-mb-n2.ssh-keys-list
|
||||
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.-gl-mb-2.ssh-keys-list
|
||||
%thead.gl-hidden.md:gl-table-header-group
|
||||
%tr
|
||||
%th= s_('Profiles|Key')
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
- if @keys.any?
|
||||
.table-holder
|
||||
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.gl-mb-n2.ssh-keys-list{ data: { testid: 'ssh-keys-list' } }
|
||||
%table.table.b-table.gl-table.b-table-stacked-md.-gl-mt-1.-gl-mb-2.ssh-keys-list{ data: { testid: 'ssh-keys-list' } }
|
||||
%thead.gl-hidden.md:gl-table-header-group
|
||||
%tr
|
||||
%th= _('Title')
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
= link_to _('Edit file'), edit_blob_path(@user.user_project, @user.user_project.default_branch, @user.user_readme.path)
|
||||
= render 'projects/blob/viewer', viewer: @user.user_readme.rich_viewer, load_async: false
|
||||
.js-read-more-trigger.read-more-trigger.gl-h-8.gl-absolute.gl-z-2.gl-bg-white.gl-px-6.gl-rounded-bottom-base.gl-cursor-pointer
|
||||
= render Pajamas::ButtonComponent.new(variant: :link, button_text_classes: 'gl-display-flex gl-align-items-center gl-gap-1', button_options: { class: 'gl-mt-4 gl-ml-n1', 'aria-label': _("Expand Readme") }) do
|
||||
= render Pajamas::ButtonComponent.new(variant: :link, button_text_classes: 'gl-display-flex gl-align-items-center gl-gap-1', button_options: { class: 'gl-mt-4 -gl-ml-1', 'aria-label': _("Expand Readme") }) do
|
||||
= sprite_icon('chevron-down', size: 14)
|
||||
= _("Read more")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
description: User clicks "issues assigned to me"
|
||||
internal_events: true
|
||||
action: click_issues_assigned_to_me_in_command_palette
|
||||
identifiers:
|
||||
- namespace
|
||||
- user
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
description: User clicks "issues I created"
|
||||
internal_events: true
|
||||
action: click_issues_i_created_in_command_palette
|
||||
identifiers:
|
||||
- namespace
|
||||
- user
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
description: User clicks "merge requests assigned to me"
|
||||
internal_events: true
|
||||
action: click_merge_requests_assigned_to_me_in_command_palette
|
||||
identifiers:
|
||||
- namespace
|
||||
- user
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
description: User clicks "merge requests I created"
|
||||
internal_events: true
|
||||
action: click_merge_requests_i_created_in_command_palette
|
||||
identifiers:
|
||||
- namespace
|
||||
- user
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
description: User clicks "merge reqeusts that I'm a reviewer"
|
||||
internal_events: true
|
||||
action: click_merge_requests_that_im_a_reviewer_in_command_palette
|
||||
identifiers:
|
||||
- namespace
|
||||
- user
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
distributions:
|
||||
- ce
|
||||
- ee
|
||||
tiers:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
@ -8,7 +8,7 @@ identifiers:
|
|||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
milestone: '17.0'
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151645
|
||||
distributions:
|
||||
- ce
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: refactor_auto_merge
|
||||
feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/10874
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/146153
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/443872
|
||||
milestone: '16.11'
|
||||
group: group::code review
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: use_ids_for_markdown_upload_urls
|
||||
feature_issue_url: https://gitlab.com/gitlab-sirt/shared-incidents/incident_5281/-/work_items/6
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150939
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/461757
|
||||
milestone: '17.0'
|
||||
group: group::project management
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -47,7 +47,6 @@ options:
|
|||
- p_ci_templates_security_license_scanning
|
||||
- p_ci_templates_security_coverage_fuzzing
|
||||
- p_ci_templates_security_coverage_fuzzing_latest
|
||||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_secure_binaries
|
||||
- p_ci_templates_security_dast_api
|
||||
- p_ci_templates_security_dast_api_latest
|
||||
|
|
@ -56,6 +55,9 @@ options:
|
|||
- p_ci_templates_security_dast_latest
|
||||
- p_ci_templates_security_dependency_scanning
|
||||
- p_ci_templates_security_api_fuzzing
|
||||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_api_security
|
||||
- p_ci_templates_security_api_security_latest
|
||||
- p_ci_templates_security_dast
|
||||
- p_ci_templates_security_api_discovery
|
||||
- p_ci_templates_security_bas_latest
|
||||
|
|
@ -170,6 +172,9 @@ options:
|
|||
- p_ci_templates_implicit_security_dast_latest
|
||||
- p_ci_templates_implicit_security_dependency_scanning
|
||||
- p_ci_templates_implicit_security_api_fuzzing
|
||||
- p_ci_templates_implicit_security_api_fuzzing_latest
|
||||
- p_ci_templates_implicit_security_api_security
|
||||
- p_ci_templates_implicit_security_api_security_latest
|
||||
- p_ci_templates_implicit_security_dast
|
||||
- p_ci_templates_implicit_security_sast_iac
|
||||
- p_ci_templates_kaniko
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_api_security_latest_monthly
|
||||
description: Monthly counts for API Security latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147183
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_api_security_latest
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_api_security_monthly
|
||||
description: Monthly counts for API Security CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147183
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_api_security
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_issues_assigned_to_me_in_command_palette_monthly
|
||||
description: Monthly count of unique users User clicks "issues assigned to me"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_issues_assigned_to_me_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_issues_i_created_in_command_palette_monthly
|
||||
description: Monthly count of unique users User clicks "issues I created"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_issues_i_created_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_assigned_to_me_in_command_palette_monthly
|
||||
description: Monthly count of unique users User clicks "merge requests assigned to me"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_assigned_to_me_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_i_created_in_command_palette_monthly
|
||||
description: Monthly count of unique users User clicks "merge requests I created"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_i_created_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_that_im_a_reviewer_in_command_palette_monthly
|
||||
description: Monthly count of unique users User clicks "merge reqeusts that I'm a reviewer"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_that_im_a_reviewer_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -7,7 +7,7 @@ product_group: global_search
|
|||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.0'
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151645
|
||||
time_frame: 28d
|
||||
data_source: internal_events
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ options:
|
|||
- p_ci_templates_security_license_scanning
|
||||
- p_ci_templates_security_coverage_fuzzing
|
||||
- p_ci_templates_security_coverage_fuzzing_latest
|
||||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_secure_binaries
|
||||
- p_ci_templates_security_dast_api
|
||||
- p_ci_templates_security_dast_api_latest
|
||||
|
|
@ -56,6 +55,9 @@ options:
|
|||
- p_ci_templates_security_dast_latest
|
||||
- p_ci_templates_security_dependency_scanning
|
||||
- p_ci_templates_security_api_fuzzing
|
||||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_api_security
|
||||
- p_ci_templates_security_api_security_latest
|
||||
- p_ci_templates_security_dast
|
||||
- p_ci_templates_security_api_discovery
|
||||
- p_ci_templates_security_bas_latest
|
||||
|
|
@ -172,6 +174,9 @@ options:
|
|||
- p_ci_templates_implicit_security_dast_latest
|
||||
- p_ci_templates_implicit_security_dependency_scanning
|
||||
- p_ci_templates_implicit_security_api_fuzzing
|
||||
- p_ci_templates_implicit_security_api_fuzzing_latest
|
||||
- p_ci_templates_implicit_security_api_security
|
||||
- p_ci_templates_implicit_security_api_security_latest
|
||||
- p_ci_templates_implicit_security_dast
|
||||
- p_ci_templates_kaniko
|
||||
- p_ci_templates_qualys_iac_security
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_api_security_latest_weekly
|
||||
description: Weekly counts for API Security latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "17.1"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147183
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_api_security_latest
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_api_security_weekly
|
||||
description: Weekly counts for API Security CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "17.1"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147183
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_api_security
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_issues_assigned_to_me_in_command_palette_weekly
|
||||
description: Weekly count of unique users who click "issues assigned to me"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_issues_assigned_to_me_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_issues_i_created_in_command_palette_weekly
|
||||
description: Weekly count of unique users who click "issues I created"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_issues_i_created_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_assigned_to_me_in_command_palette_weekly
|
||||
description: Weekly count of unique users who click "merge requests assigned to me"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_assigned_to_me_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_i_created_in_command_palette_weekly
|
||||
description: Weekly count of unique users who click "merge requests I created"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_i_created_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.count_distinct_user_id_from_click_merge_requests_that_im_a_reviewer_in_command_palette_weekly
|
||||
description: Weekly count of unique users who click "merge reqeusts that I'm a reviewer"
|
||||
product_section: core_platform
|
||||
product_stage: data_stores
|
||||
product_group: global_search
|
||||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147919
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
data_category: optional
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
events:
|
||||
- name: click_merge_requests_that_im_a_reviewer_in_command_palette
|
||||
unique: user.id
|
||||
|
|
@ -7,7 +7,7 @@ product_group: global_search
|
|||
performance_indicator_type: []
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '17.0'
|
||||
milestone: '17.1'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151645
|
||||
time_frame: 7d
|
||||
data_source: internal_events
|
||||
|
|
|
|||
|
|
@ -108,7 +108,8 @@ InitializerConnections.raise_if_new_database_connection do
|
|||
constraints: {
|
||||
model: /project|group/,
|
||||
filename: %r{[^/]+}
|
||||
}
|
||||
},
|
||||
as: 'banzai_upload'
|
||||
|
||||
get '/whats_new' => 'whats_new#index'
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,27 @@ In exceptional cases, we may need to add a new bounded context to the list. This
|
|||
- We are introducing a new product category that does not align with any existing bounded contexts.
|
||||
- We are extracting a bounded context out of an existing one because it's too large and we want to decouple the two.
|
||||
|
||||
### GitLab/BoundedContexts and `config/bounded_contexts.yml` FAQ
|
||||
|
||||
1. **Is there ever a situation where the cop should be disabled?**
|
||||
|
||||
- The cop **should not** be disabled but it **could** be disabled temporarily if the offending class or module is part
|
||||
of a cluster of classes that should otherwise be moved all together.
|
||||
In this case you could disable the cop and create a follow-up issue to move all the classes at once.
|
||||
1. **Is there a suggested timeline to get all of the existing code refactored into compliance?**
|
||||
|
||||
- We do not have a timeline defined but the quicker we consolidate code the more consistent it becomes.
|
||||
1. **Do the bounded contexts apply for existing Sidekiq workers?**
|
||||
|
||||
- Existing workers would be already in the RuboCop TODO file so they do not raise offenses. However, they should
|
||||
also be moved into the bounded context whenever possible.
|
||||
Follow the Sidekiq [renaming worker](../development/sidekiq/compatibility_across_updates.md#renaming-worker-classes) guide.
|
||||
1. **We are renaming a feature category and the `config/bounded_contexts.yml` references that. Is it safe to update?**
|
||||
|
||||
- Yes the file only expects that the feature categories mapped to bounded contexts are defined in `config/feature_categories.yml`
|
||||
and nothing specifically depends on these values. This mapping is primarily for contributors to understand where features
|
||||
may be living in the codebase.
|
||||
|
||||
## Distinguish domain code from generic code
|
||||
|
||||
The [guidelines above](#use-namespaces-to-define-bounded-contexts) refer primarily to the domain code.
|
||||
|
|
|
|||
|
|
@ -7880,7 +7880,7 @@
|
|||
canonical: |
|
||||
<p><a href="groups-test-file">groups-test-file</a></p>
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:45" dir="auto"><a data-sourcepos="1:1-1:45" href="/groups/glfm_group/-/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
|
||||
<p data-sourcepos="1:1-1:45" dir="auto"><a data-sourcepos="1:1-1:45" href="/-/group/66666/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
|
||||
08_03_00__gitlab_internal_extension_markdown__markdown_preview_api_request_overrides__002:
|
||||
canonical: |
|
||||
<p><a href="projects-test-file">projects-test-file</a></p>
|
||||
|
|
@ -7910,14 +7910,14 @@
|
|||
canonical: |
|
||||
TODO: Write canonical HTML for this example
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img data-sourcepos="1:1-1:69" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
|
||||
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/-/group/66666/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img data-sourcepos="1:1-1:69" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/-/group/66666/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
|
||||
wysiwyg: |-
|
||||
<p dir="auto"><img src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" alt="test-file"></p>
|
||||
08_04_02__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project__001:
|
||||
canonical: |
|
||||
TODO: Write canonical HTML for this example
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img data-sourcepos="1:1-1:69" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
|
||||
<p data-sourcepos="1:1-1:69" dir="auto"><a class="no-attachment-icon gfm" href="/-/project/77777/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" target="_blank" rel="noopener noreferrer" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-link="true"><img data-sourcepos="1:1-1:69" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="test-file" decoding="async" class="lazy gfm" data-src="/-/project/77777/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png"></a></p>
|
||||
wysiwyg: |-
|
||||
<p dir="auto"><img src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.png" alt="test-file"></p>
|
||||
08_04_03__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_image_for_project_wiki__001:
|
||||
|
|
@ -7931,14 +7931,14 @@
|
|||
canonical: |
|
||||
TODO: Write canonical HTML for this example
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:68" dir="auto"><a data-sourcepos="1:1-1:68" href="/groups/glfm_group/-/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
|
||||
<p data-sourcepos="1:1-1:68" dir="auto"><a data-sourcepos="1:1-1:68" href="/-/group/66666/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
|
||||
wysiwyg: |-
|
||||
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip">test-file</a></p>
|
||||
08_04_05__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project__001:
|
||||
canonical: |
|
||||
TODO: Write canonical HTML for this example
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:68" dir="auto"><a data-sourcepos="1:1-1:68" href="/glfm_group/glfm_project/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
|
||||
<p data-sourcepos="1:1-1:68" dir="auto"><a data-sourcepos="1:1-1:68" href="/-/project/77777/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-canonical-src="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip" data-link="true" class="gfm">test-file</a></p>
|
||||
wysiwyg: |-
|
||||
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/aa45a38ec2cfe97433281b10bbff042c/test-file.zip">test-file</a></p>
|
||||
08_04_06__gitlab_internal_extension_markdown__migrated_golden_master_examples__attachment_link_for_project_wiki__001:
|
||||
|
|
|
|||
|
|
@ -8,11 +8,20 @@ module API
|
|||
expose :markdown_name, as: :alt
|
||||
expose :secure_url, as: :url
|
||||
expose :full_path do |uploader|
|
||||
show_project_uploads_path(
|
||||
uploader.model,
|
||||
uploader.secret,
|
||||
uploader.filename
|
||||
)
|
||||
if ::Feature.enabled?(:use_ids_for_markdown_upload_urls, uploader.model)
|
||||
banzai_upload_path(
|
||||
'project',
|
||||
uploader.model.id,
|
||||
uploader.secret,
|
||||
uploader.filename
|
||||
)
|
||||
else
|
||||
show_project_uploads_path(
|
||||
uploader.model,
|
||||
uploader.secret,
|
||||
uploader.filename
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
expose :markdown_link, as: :markdown
|
||||
|
|
|
|||
|
|
@ -32,9 +32,17 @@ module Banzai
|
|||
path_parts = [unescape_and_scrub_uri(html_attr.value)]
|
||||
|
||||
if project
|
||||
path_parts.unshift(relative_url_root, project.full_path)
|
||||
if Feature.enabled?(:use_ids_for_markdown_upload_urls, project)
|
||||
path_parts.unshift(relative_url_root, '-', 'project', project.id.to_s)
|
||||
else
|
||||
path_parts.unshift(relative_url_root, project.full_path)
|
||||
end
|
||||
elsif group
|
||||
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
|
||||
if Feature.enabled?(:use_ids_for_markdown_upload_urls, group)
|
||||
path_parts.unshift(relative_url_root, '-', 'group', group.id.to_s)
|
||||
else
|
||||
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
|
||||
end
|
||||
else
|
||||
path_parts.unshift(relative_url_root)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.gitlab-ci.yml
|
||||
|
||||
# To use this template, add the following to your .gitlab-ci.yml file:
|
||||
#
|
||||
# include:
|
||||
# template: API-Security.gitlab-ci.yml
|
||||
#
|
||||
# You also need to add a `dast` stage to your `stages:` configuration. A sample configuration for DAST API:
|
||||
#
|
||||
# stages:
|
||||
# - build
|
||||
# - test
|
||||
# - deploy
|
||||
# - dast
|
||||
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html
|
||||
|
||||
# Configure API Security scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
|
||||
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html#available-cicd-variables
|
||||
|
||||
variables:
|
||||
# Setting this variable affects all Security templates
|
||||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
|
||||
#
|
||||
APISEC_VERSION: "5"
|
||||
APISEC_IMAGE_SUFFIX: ""
|
||||
APISEC_IMAGE: api-security
|
||||
|
||||
api_security:
|
||||
stage: dast
|
||||
image: $SECURE_ANALYZERS_PREFIX/$APISEC_IMAGE:$APISEC_VERSION$APISEC_IMAGE_SUFFIX
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $APISEC_DISABLED == 'true' || $APISEC_DISABLED == '1'
|
||||
when: never
|
||||
- if: $APISEC_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
|
||||
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
|
||||
when: never
|
||||
- if: $APISEC_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
|
||||
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$CI_GITLAB_FIPS_MODE == "true"
|
||||
variables:
|
||||
APISEC_IMAGE_SUFFIX: "-fips"
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
script:
|
||||
- /peach/analyzer-api-security
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- gl-assets
|
||||
- gl-api-security-report.json
|
||||
- gl-*.log
|
||||
reports:
|
||||
dast: gl-api-security-report.json
|
||||
|
||||
# end
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.latest.gitlab-ci.yml
|
||||
|
||||
# To use this template, add the following to your .gitlab-ci.yml file:
|
||||
#
|
||||
# include:
|
||||
# template: API-Security.latest.gitlab-ci.yml
|
||||
#
|
||||
# You also need to add a `dast` stage to your `stages:` configuration. A sample configuration for DAST API:
|
||||
#
|
||||
# stages:
|
||||
# - build
|
||||
# - test
|
||||
# - deploy
|
||||
# - dast
|
||||
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html
|
||||
|
||||
# Configure API Security scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
|
||||
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html#available-cicd-variables
|
||||
|
||||
variables:
|
||||
# Setting this variable affects all Security templates
|
||||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
|
||||
#
|
||||
APISEC_VERSION: "5"
|
||||
APISEC_IMAGE_SUFFIX: ""
|
||||
APISEC_IMAGE: api-security
|
||||
|
||||
api_security:
|
||||
stage: dast
|
||||
image: $SECURE_ANALYZERS_PREFIX/$APISEC_IMAGE:$APISEC_VERSION$APISEC_IMAGE_SUFFIX
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $APISEC_DISABLED == 'true' || $APISEC_DISABLED == '1'
|
||||
when: never
|
||||
- if: $APISEC_DISABLED_FOR_DEFAULT_BRANCH == 'true' &&
|
||||
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
|
||||
when: never
|
||||
- if: $APISEC_DISABLED_FOR_DEFAULT_BRANCH == '1' &&
|
||||
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
|
||||
when: never
|
||||
|
||||
# Add the job to merge request pipelines if there's an open merge request.
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
|
||||
$CI_GITLAB_FIPS_MODE == "true"
|
||||
variables:
|
||||
APISEC_IMAGE_SUFFIX: "-fips"
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
# Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
|
||||
- if: $CI_OPEN_MERGE_REQUESTS
|
||||
when: never
|
||||
|
||||
# Add the job to branch pipelines.
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$CI_GITLAB_FIPS_MODE == "true"
|
||||
variables:
|
||||
APISEC_IMAGE_SUFFIX: "-fips"
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
script:
|
||||
- /peach/analyzer-api-security
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- gl-assets
|
||||
- gl-api-security-report.json
|
||||
- gl-*.log
|
||||
reports:
|
||||
dast: gl-api-security-report.json
|
||||
|
||||
# end
|
||||
|
|
@ -90,15 +90,10 @@ module Gitlab
|
|||
{ name: attributes[:action], time_framed?: true, filter: {} }
|
||||
]
|
||||
Gitlab::Usage::MetricDefinition.definitions.each_value do |metric_definition|
|
||||
metric_definition.attributes[:events]&.each do |event_selection_rule|
|
||||
if event_selection_rule[:name] == attributes[:action]
|
||||
result << {
|
||||
name: attributes[:action],
|
||||
time_framed?: %w[7d 28d].include?(metric_definition.attributes[:time_frame]),
|
||||
filter: event_selection_rule[:filter] || {}
|
||||
}
|
||||
end
|
||||
matching_event_selection_rules = metric_definition.event_selection_rules.select do |event_selection_rule|
|
||||
event_selection_rule[:name] == attributes[:action]
|
||||
end
|
||||
result.concat(matching_event_selection_rules)
|
||||
end
|
||||
result.uniq
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ module Gitlab
|
|||
|
||||
def instrumentation_object
|
||||
instrumentation_class = "Gitlab::Usage::Metrics::Instrumentations::#{definition.instrumentation_class}"
|
||||
@instrumentation_object ||= instrumentation_class.constantize.new(definition.attributes)
|
||||
@instrumentation_object ||= instrumentation_class.constantize.new(definition.raw_attributes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ module Gitlab
|
|||
InvalidError = Class.new(RuntimeError)
|
||||
|
||||
attr_reader :path
|
||||
attr_reader :attributes
|
||||
|
||||
def initialize(path, opts = {})
|
||||
@path = path
|
||||
|
|
@ -19,28 +18,49 @@ module Gitlab
|
|||
end
|
||||
|
||||
def key
|
||||
attributes[:key_path]
|
||||
@attributes[:key_path]
|
||||
end
|
||||
alias_method :key_path, :key
|
||||
|
||||
def events
|
||||
events_from_new_structure || events_from_old_structure || {}
|
||||
end
|
||||
|
||||
def event_selection_rules
|
||||
return [] unless @attributes[:events]
|
||||
|
||||
@attributes[:events].map do |event|
|
||||
{
|
||||
name: event[:name],
|
||||
time_framed?: time_framed?,
|
||||
filter: event[:filter] || {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def instrumentation_class
|
||||
if internal_events?
|
||||
events.each_value.first.nil? ? "TotalCountMetric" : "RedisHLLMetric"
|
||||
else
|
||||
attributes[:instrumentation_class]
|
||||
@attributes[:instrumentation_class]
|
||||
end
|
||||
end
|
||||
|
||||
# This method can be removed when the refactoring is complete. It is only here to
|
||||
# limit access to @attributes in a gradual manner.
|
||||
def raw_attributes
|
||||
@attributes
|
||||
end
|
||||
|
||||
def status
|
||||
attributes[:status]
|
||||
@attributes[:status]
|
||||
end
|
||||
|
||||
def value_json_schema
|
||||
attributes[:value_json_schema]
|
||||
@attributes[:value_json_schema]
|
||||
end
|
||||
|
||||
def value_type
|
||||
@attributes[:value_type]
|
||||
end
|
||||
|
||||
def to_context
|
||||
|
|
@ -50,7 +70,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def to_h
|
||||
attributes
|
||||
@attributes
|
||||
end
|
||||
|
||||
def json_schema
|
||||
|
|
@ -62,15 +82,15 @@ module Gitlab
|
|||
def json_schema_path
|
||||
return '' unless has_json_schema?
|
||||
|
||||
Rails.root.join(attributes[:value_json_schema])
|
||||
Rails.root.join(@attributes[:value_json_schema])
|
||||
end
|
||||
|
||||
def has_json_schema?
|
||||
attributes[:value_type] == 'object' && attributes[:value_json_schema].present?
|
||||
@attributes[:value_type] == 'object' && @attributes[:value_json_schema].present?
|
||||
end
|
||||
|
||||
def validation_errors
|
||||
SCHEMA.validate(attributes.deep_stringify_keys).map do |error|
|
||||
SCHEMA.validate(@attributes.deep_stringify_keys).map do |error|
|
||||
<<~ERROR_MSG
|
||||
--------------- VALIDATION ERROR ---------------
|
||||
Metric file: #{path}
|
||||
|
|
@ -81,16 +101,40 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def product_group
|
||||
@attributes[:product_group]
|
||||
end
|
||||
|
||||
def time_frame
|
||||
@attributes[:time_frame]
|
||||
end
|
||||
|
||||
def time_framed?
|
||||
%w[7d 28d].include?(time_frame)
|
||||
end
|
||||
|
||||
def active?
|
||||
status == 'active'
|
||||
end
|
||||
|
||||
def broken?
|
||||
status == 'broken'
|
||||
end
|
||||
|
||||
def available?
|
||||
AVAILABLE_STATUSES.include?(attributes[:status])
|
||||
AVAILABLE_STATUSES.include?(status)
|
||||
end
|
||||
|
||||
def valid_service_ping_status?
|
||||
VALID_SERVICE_PING_STATUSES.include?(attributes[:status])
|
||||
VALID_SERVICE_PING_STATUSES.include?(status)
|
||||
end
|
||||
|
||||
def data_category
|
||||
@attributes[:data_category]
|
||||
end
|
||||
|
||||
def data_source
|
||||
attributes[:data_source]
|
||||
@attributes[:data_source]
|
||||
end
|
||||
|
||||
def internal_events?
|
||||
|
|
@ -113,12 +157,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def not_removed
|
||||
all.select { |definition| definition.attributes[:status] != 'removed' }.index_by(&:key_path)
|
||||
all.select { |definition| definition.status != 'removed' }.index_by(&:key_path)
|
||||
end
|
||||
|
||||
def with_instrumentation_class
|
||||
all.select do |definition|
|
||||
(definition.internal_events? || definition.attributes[:instrumentation_class].present?) && definition.available?
|
||||
(definition.internal_events? || definition.instrumentation_class.present?) && definition.available?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -164,14 +208,14 @@ module Gitlab
|
|||
private
|
||||
|
||||
def events_from_new_structure
|
||||
events = attributes[:events]
|
||||
events = @attributes[:events]
|
||||
return unless events
|
||||
|
||||
events.to_h { |event| [event[:name], event[:unique]&.to_sym] }
|
||||
end
|
||||
|
||||
def events_from_old_structure
|
||||
options_events = attributes.dig(:options, :events)
|
||||
options_events = @attributes.dig(:options, :events)
|
||||
return unless options_events
|
||||
|
||||
options_events.index_with { nil }
|
||||
|
|
|
|||
|
|
@ -486,6 +486,8 @@
|
|||
- p_ci_templates_security_api_discovery
|
||||
- p_ci_templates_security_api_fuzzing
|
||||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_api_security
|
||||
- p_ci_templates_security_api_security_latest
|
||||
- p_ci_templates_security_bas_latest
|
||||
- p_ci_templates_security_container_scanning
|
||||
- p_ci_templates_security_container_scanning_latest
|
||||
|
|
|
|||
|
|
@ -47,9 +47,7 @@ module ServicePing
|
|||
end
|
||||
|
||||
def metric_category(key_path)
|
||||
metric_definitions[key_path]
|
||||
&.attributes
|
||||
&.fetch(:data_category, ::ServicePing::PermitDataCategories::OPTIONAL_CATEGORY)
|
||||
metric_definitions[key_path]&.data_category || ::ServicePing::PermitDataCategories::OPTIONAL_CATEGORY
|
||||
end
|
||||
|
||||
def metric_definitions
|
||||
|
|
|
|||
|
|
@ -33099,6 +33099,15 @@ msgstr ""
|
|||
msgid "MlExperimentTracking|Version"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|# Create a model"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|# Create a version"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|# Log artifacts"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|%d model"
|
||||
msgid_plural "MlModelRegistry|%d models"
|
||||
msgstr[0] ""
|
||||
|
|
@ -33217,6 +33226,9 @@ msgstr ""
|
|||
msgid "MlModelRegistry|ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|Import your machine learning using GitLab directly or using the MLflow client:"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|Info"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33232,6 +33244,9 @@ msgstr ""
|
|||
msgid "MlModelRegistry|Leave empty to skip version creation."
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|MLflow compatibility"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|MLflow run ID"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -33277,6 +33292,9 @@ msgstr ""
|
|||
msgid "MlModelRegistry|No logged parameters"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|No models registered"
|
||||
msgstr ""
|
||||
|
||||
msgid "MlModelRegistry|No registered versions"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ require 'spec_helper'
|
|||
RSpec.describe Groups::ChildrenController, feature_category: :groups_and_projects do
|
||||
include ExternalAuthorizationServiceHelpers
|
||||
|
||||
let(:group) { create(:group, :public) }
|
||||
let(:user) { create(:user) }
|
||||
let!(:group_member) { create(:group_member, group: group, user: user) }
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be_with_reload(:group_member) { create(:group_member, group: group, user: user) }
|
||||
|
||||
describe 'GET #index' do
|
||||
context 'for projects' do
|
||||
let!(:public_project) { create(:project, :public, namespace: group) }
|
||||
let!(:private_project) { create(:project, :private, namespace: group) }
|
||||
let_it_be(:public_project) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:private_project) { create(:project, :private, namespace: group) }
|
||||
|
||||
context 'as a user' do
|
||||
before do
|
||||
|
|
@ -47,10 +47,10 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
|
||||
context 'for subgroups' do
|
||||
let!(:public_subgroup) { create(:group, :public, parent: group) }
|
||||
let!(:private_subgroup) { create(:group, :private, parent: group) }
|
||||
let!(:public_project) { create(:project, :public, namespace: group) }
|
||||
let!(:private_project) { create(:project, :private, namespace: group) }
|
||||
let_it_be(:public_subgroup) { create(:group, :public, parent: group) }
|
||||
let_it_be(:private_subgroup) { create(:group, :private, parent: group) }
|
||||
let_it_be(:public_project) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:private_project) { create(:project, :private, namespace: group) }
|
||||
|
||||
context 'as a user' do
|
||||
before do
|
||||
|
|
@ -197,6 +197,17 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
end
|
||||
|
||||
context 'sorting children' do
|
||||
it 'allows sorting projects' do
|
||||
project_1 = create(:project, :public, namespace: group, name: 'mobile')
|
||||
project_2 = create(:project, :public, namespace: group, name: 'hardware')
|
||||
|
||||
get :index, params: { group_id: group.to_param, sort: 'name_asc' }, format: :json
|
||||
|
||||
expect(assigns(:children)).to eq([public_subgroup, project_2, project_1, public_project])
|
||||
end
|
||||
end
|
||||
|
||||
context 'queries per rendered element', :request_store do
|
||||
# We need to make sure the following counts are preloaded
|
||||
# otherwise they will cause an extra query
|
||||
|
|
@ -269,7 +280,7 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
|
||||
context 'pagination' do
|
||||
let(:per_page) { 3 }
|
||||
let_it_be(:per_page) { 3 }
|
||||
|
||||
before do
|
||||
allow(Kaminari.config).to receive(:default_per_page).and_return(per_page)
|
||||
|
|
@ -288,8 +299,8 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
|
||||
context 'with only projects' do
|
||||
let!(:other_project) { create(:project, :public, namespace: group) }
|
||||
let!(:first_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
let_it_be(:other_project) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:first_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
|
||||
it 'has projects on the first page' do
|
||||
get :index, params: { group_id: group.to_param, sort: 'id_desc' }, format: :json
|
||||
|
|
@ -305,9 +316,9 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
|
||||
context 'with subgroups and projects' do
|
||||
let!(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) }
|
||||
let!(:other_subgroup) { create(:group, :public, parent: group) }
|
||||
let!(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
let_it_be(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) }
|
||||
let_it_be(:other_subgroup) { create(:group, :public, parent: group) }
|
||||
let_it_be(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
|
||||
it 'contains all subgroups' do
|
||||
get :index, params: { group_id: group.to_param, sort: 'id_asc' }, format: :json
|
||||
|
|
@ -322,8 +333,8 @@ RSpec.describe Groups::ChildrenController, feature_category: :groups_and_project
|
|||
end
|
||||
|
||||
context 'with a mixed first page' do
|
||||
let!(:first_page_subgroups) { [create(:group, :public, parent: group)] }
|
||||
let!(:first_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
let_it_be(:first_page_subgroups) { [create(:group, :public, parent: group)] }
|
||||
let_it_be(:first_page_projects) { create_list(:project, per_page, :public, namespace: group) }
|
||||
|
||||
it 'correctly calculates the counts' do
|
||||
get :index, params: { group_id: group.to_param, sort: 'id_asc', page: 2 }, format: :json
|
||||
|
|
|
|||
|
|
@ -656,7 +656,7 @@ RSpec.describe 'Copy as GFM', :js, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
def project_media_uri(project, media_path)
|
||||
"#{project_path(project)}#{media_path}"
|
||||
"/-/project/#{project.id}#{media_path}"
|
||||
end
|
||||
|
||||
def verify_media_with_partial_path(gfm, media_uri)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ RSpec.describe 'Projects > Snippets > Create Snippet', :js, feature_category: :s
|
|||
wait_for_requests
|
||||
|
||||
link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
|
||||
expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
|
||||
expect(link).to match(%r{/-/project/#{project.id}/uploads/\h{32}/banana_sample\.gif\z})
|
||||
end
|
||||
|
||||
context 'when the git operation fails' do
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ RSpec.describe 'User uploads file to note', feature_category: :team_planning do
|
|||
wait_for_requests
|
||||
|
||||
expect(find('a.no-attachment-icon img.js-lazy-loaded[alt="dk"]')['src'])
|
||||
.to match(%r{/#{project.full_path}/uploads/\h{32}/dk\.png$})
|
||||
.to match(%r{/-/project/#{project.id}/uploads/\h{32}/dk\.png$})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import { IndexMlModels } from '~/ml/model_registry/apps';
|
||||
import ModelRow from '~/ml/model_registry/components/model_row.vue';
|
||||
import ModelCreate from '~/ml/model_registry/components/model_create.vue';
|
||||
import { MODEL_ENTITIES } from '~/ml/model_registry/constants';
|
||||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
|
||||
import EmptyState from '~/ml/model_registry/components/empty_state.vue';
|
||||
import EmptyState from '~/ml/model_registry/components/model_list_empty_state.vue';
|
||||
import ActionsDropdown from '~/ml/model_registry/components/actions_dropdown.vue';
|
||||
import SearchableList from '~/ml/model_registry/components/searchable_list.vue';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
|
@ -21,7 +20,6 @@ Vue.use(VueApollo);
|
|||
|
||||
const defaultProps = {
|
||||
projectPath: 'path/to/project',
|
||||
createModelPath: 'path/to/create',
|
||||
canWriteModelRegistry: false,
|
||||
};
|
||||
|
||||
|
|
@ -89,7 +87,18 @@ describe('ml/model_registry/apps/index_ml_models', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findEmptyState().props('entityType')).toBe(MODEL_ENTITIES.model);
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('opens model creation from empty state', async () => {
|
||||
createWrapper({
|
||||
props: { canWriteModelRegistry: true },
|
||||
resolver: emptyQueryResolver(),
|
||||
});
|
||||
|
||||
await findEmptyState().vm.$emit('open-create-model');
|
||||
|
||||
expect(findModelCreate().props('createModelVisible')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -115,6 +124,21 @@ describe('ml/model_registry/apps/index_ml_models', () => {
|
|||
|
||||
expect(findModelCreate().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('opens and closes model creation', async () => {
|
||||
createWrapper({
|
||||
props: { canWriteModelRegistry: true },
|
||||
resolver: emptyQueryResolver(),
|
||||
});
|
||||
|
||||
await findModelCreate().vm.$emit('show-create-model');
|
||||
|
||||
expect(findModelCreate().props('createModelVisible')).toBe(true);
|
||||
|
||||
await findModelCreate().vm.$emit('hide-create-model');
|
||||
|
||||
expect(findModelCreate().props('createModelVisible')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,39 +1,29 @@
|
|||
import axios from 'axios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { createAlert } from '~/alert';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { uploadModel } from '~/ml/model_registry/services/upload_model';
|
||||
import ImportArtifactZone from '~/ml/model_registry/components/import_artifact_zone.vue';
|
||||
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
|
||||
|
||||
jest.mock('~/alert');
|
||||
jest.mock('~/ml/model_registry/services/upload_model', () => ({
|
||||
uploadModel: jest.fn(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
describe('ImportArtifactZone', () => {
|
||||
let wrapper;
|
||||
let axiosMock;
|
||||
|
||||
const file = { name: 'file.txt', size: 1024 };
|
||||
const initialProps = {
|
||||
path: 'some/path',
|
||||
};
|
||||
const filePath = 'some/path/file.txt';
|
||||
const formattedFileSizeDiv = () => wrapper.findByTestId('formatted-file-size');
|
||||
const fileNameDiv = () => wrapper.findByTestId('file-name');
|
||||
const zone = () => wrapper.findComponent(UploadDropzone);
|
||||
const loadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const emulateFileDrop = () => zone().vm.$emit('change', file);
|
||||
|
||||
beforeEach(() => {
|
||||
axiosMock = new MockAdapter(axios);
|
||||
axiosMock.onPut(filePath).replyOnce(HTTP_STATUS_OK, {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
axiosMock.restore();
|
||||
});
|
||||
|
||||
describe('successful upload', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMountExtended(ImportArtifactZone, {
|
||||
|
|
@ -72,9 +62,13 @@ describe('ImportArtifactZone', () => {
|
|||
await emulateFileDrop();
|
||||
await waitForPromises();
|
||||
|
||||
expect(axiosMock.history.put).toHaveLength(1);
|
||||
const uploadRequest = axiosMock.history.put[0];
|
||||
expect(uploadRequest.url).toBe('some/path/file.txt');
|
||||
expect(uploadModel).toHaveBeenCalledWith({
|
||||
file: {
|
||||
name: 'file.txt',
|
||||
size: 1024,
|
||||
},
|
||||
importPath: 'some/path',
|
||||
});
|
||||
});
|
||||
|
||||
it('emits a change event on success', async () => {
|
||||
|
|
@ -94,8 +88,7 @@ describe('ImportArtifactZone', () => {
|
|||
});
|
||||
|
||||
it('displays an error on failure', async () => {
|
||||
axiosMock.reset();
|
||||
axiosMock.onPut(filePath).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR, {});
|
||||
uploadModel.mockRejectedValue('Internal server error.');
|
||||
|
||||
await emulateFileDrop();
|
||||
await waitForPromises();
|
||||
|
|
@ -106,8 +99,7 @@ describe('ImportArtifactZone', () => {
|
|||
});
|
||||
|
||||
it('resets the state on failure', async () => {
|
||||
axiosMock.reset();
|
||||
axiosMock.onPut(filePath).timeout();
|
||||
uploadModel.mockRejectedValue('Internal server error.');
|
||||
|
||||
await emulateFileDrop();
|
||||
await waitForPromises();
|
||||
|
|
@ -131,7 +123,7 @@ describe('ImportArtifactZone', () => {
|
|||
await emulateFileDrop();
|
||||
await waitForPromises();
|
||||
|
||||
expect(axiosMock.history.put).toHaveLength(0);
|
||||
expect(uploadModel).not.toHaveBeenCalled();
|
||||
expect(loadingIcon().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -150,7 +142,7 @@ describe('ImportArtifactZone', () => {
|
|||
await emulateFileDrop();
|
||||
await waitForPromises();
|
||||
|
||||
expect(axiosMock.history.put).toHaveLength(0);
|
||||
expect(uploadModel).not.toHaveBeenCalled();
|
||||
expect(loadingIcon().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ describe('ModelCreate', () => {
|
|||
const createWrapper = (
|
||||
createModelResolver = jest.fn().mockResolvedValue(createModelResponses.success),
|
||||
createModelVersionResolver = jest.fn().mockResolvedValue(createModelVersionResponses.success),
|
||||
createModelVisible = false,
|
||||
) => {
|
||||
const requestHandlers = [
|
||||
[createModelMutation, createModelResolver],
|
||||
|
|
@ -48,6 +49,9 @@ describe('ModelCreate', () => {
|
|||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
||||
wrapper = shallowMountExtended(ModelCreate, {
|
||||
propsData: {
|
||||
createModelVisible,
|
||||
},
|
||||
provide: {
|
||||
projectPath: 'some/project',
|
||||
},
|
||||
|
|
@ -69,17 +73,37 @@ describe('ModelCreate', () => {
|
|||
};
|
||||
|
||||
describe('Initial state', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
describe('Modal closed', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
it('renders the modal button', () => {
|
||||
expect(findModalButton().text()).toBe('Create model');
|
||||
it('does not show modal', () => {
|
||||
expect(findGlModal().props('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the modal button', () => {
|
||||
expect(findModalButton().text()).toBe('Create model');
|
||||
});
|
||||
|
||||
it('clicking create button triggers show-create-model', async () => {
|
||||
await findModalButton().vm.$emit('click');
|
||||
|
||||
expect(wrapper.emitted('show-create-model')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modal open', () => {
|
||||
beforeEach(() => {
|
||||
findModalButton().trigger('click');
|
||||
createWrapper(
|
||||
jest.fn().mockResolvedValue(createModelResponses.success),
|
||||
jest.fn().mockResolvedValue(createModelVersionResponses.success),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show modal', () => {
|
||||
expect(findGlModal().props('visible')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the name input', () => {
|
||||
|
|
@ -135,6 +159,18 @@ describe('ModelCreate', () => {
|
|||
it('does not render the alert by default', () => {
|
||||
expect(findGlAlert().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('clicking on cancel button triggers hide-create-model', async () => {
|
||||
await findGlModal().vm.$emit('cancel');
|
||||
|
||||
expect(wrapper.emitted('hide-create-model')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('dismissing modal triggers hide-create-model', async () => {
|
||||
await findGlModal().vm.$emit('hide');
|
||||
|
||||
expect(wrapper.emitted('hide-create-model')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { GlEmptyState, GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import EmptyState from '~/ml/model_registry/components/model_list_empty_state.vue';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
|
||||
let wrapper;
|
||||
const createWrapper = () => {
|
||||
wrapper = shallowMount(EmptyState, {
|
||||
provide: { mlflowTrackingUrl: 'path/to/mlflow', createModelPath: '/path/to/create' },
|
||||
});
|
||||
};
|
||||
|
||||
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
|
||||
const findCopyButton = () => wrapper.findComponent(ClipboardButton);
|
||||
const findCreateButton = () => wrapper.findComponent(GlButton);
|
||||
const findDocsButton = () => wrapper.findAllComponents(GlButton).at(1);
|
||||
|
||||
const mlflowCmd = [
|
||||
'import os',
|
||||
'from mlflow import MlflowClient',
|
||||
'',
|
||||
'os.environ["MLFLOW_TRACKING_URI"] = "path/to/mlflow"',
|
||||
'os.environ["MLFLOW_TRACKING_TOKEN"] = <your_gitlab_token>',
|
||||
'',
|
||||
'# Create a model',
|
||||
'client = MlflowClient()',
|
||||
"model_name = '<your_model_name>'",
|
||||
"description = 'Model description'",
|
||||
'model = client.create_registered_model(model_name, description=description)',
|
||||
'',
|
||||
'# Create a version',
|
||||
'tags = { "gitlab.version": version }',
|
||||
'model_version = client.create_model_version(model_name, version, tags=tags)',
|
||||
'',
|
||||
'# Log artifacts',
|
||||
'client.log_artifact(run_id, \'<local/path/to/file.txt>\', artifact_path="")',
|
||||
].join('\n');
|
||||
|
||||
describe('ml/model_registry/components/model_list_empty_state.vue', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
describe('when entity type is model', () => {
|
||||
it('shows the correct empty state', () => {
|
||||
expect(findEmptyState().props()).toMatchObject({
|
||||
title: 'No models registered',
|
||||
svgPath: 'file-mock',
|
||||
});
|
||||
|
||||
expect(findEmptyState().text()).toContain(
|
||||
'Import your machine learning using GitLab directly or using the MLflow client:',
|
||||
);
|
||||
expect(findEmptyState().text()).toContain(mlflowCmd);
|
||||
});
|
||||
|
||||
it('creates the copy text button', () => {
|
||||
expect(findCopyButton().props('text')).toBe(mlflowCmd);
|
||||
});
|
||||
|
||||
it('creates button to open model creation', () => {
|
||||
expect(findCreateButton().text()).toBe('Create model');
|
||||
});
|
||||
|
||||
it('clicking creates triggers open-create-model', async () => {
|
||||
await findCreateButton().vm.$emit('click');
|
||||
|
||||
expect(wrapper.emitted('open-create-model')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('creates button to docs', () => {
|
||||
expect(findDocsButton().text()).toBe('MLflow compatibility');
|
||||
expect(findDocsButton().attributes('href')).toBe(
|
||||
'/help/user/project/ml/model_registry/index#creating-machine-learning-models-and-model-versions',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -5,6 +5,14 @@ import Vuex from 'vuex';
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import GlobalSearchDefaultIssuables from '~/super_sidebar/components/global_search/components/global_search_default_issuables.vue';
|
||||
import SearchResultHoverLayover from '~/super_sidebar/components/global_search/components/global_search_hover_overlay.vue';
|
||||
import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper';
|
||||
import {
|
||||
ISSUES_ASSIGNED_TO_ME_TITLE,
|
||||
ISSUES_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_THAT_I_AM_A_REVIEWER,
|
||||
MERGE_REQUESTS_I_HAVE_CREATED_TITLE,
|
||||
MERGE_REQUESTS_ASSIGNED_TO_ME_TITLE,
|
||||
} from '~/super_sidebar/components/global_search/command_palette/constants';
|
||||
import {
|
||||
MOCK_SEARCH_CONTEXT,
|
||||
MOCK_PROJECT_SEARCH_CONTEXT,
|
||||
|
|
@ -158,4 +166,29 @@ describe('GlobalSearchDefaultPlaces', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Track events', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
searchContext: MOCK_PROJECT_SEARCH_CONTEXT,
|
||||
mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
});
|
||||
});
|
||||
|
||||
const { bindInternalEventDocument } = useMockInternalEventsTracking();
|
||||
|
||||
it.each`
|
||||
eventTrigger | event
|
||||
${ISSUES_ASSIGNED_TO_ME_TITLE} | ${'click_issues_assigned_to_me_in_command_palette'}
|
||||
${ISSUES_I_HAVE_CREATED_TITLE} | ${'click_issues_i_created_in_command_palette'}
|
||||
${MERGE_REQUESTS_ASSIGNED_TO_ME_TITLE} | ${'click_merge_requests_assigned_to_me_in_command_palette'}
|
||||
${MERGE_REQUESTS_THAT_I_AM_A_REVIEWER} | ${'click_merge_requests_that_im_a_reviewer_in_command_palette'}
|
||||
${MERGE_REQUESTS_I_HAVE_CREATED_TITLE} | ${'click_merge_requests_i_created_in_command_palette'}
|
||||
`('triggers and tracks command dropdown $event', ({ eventTrigger, event }) => {
|
||||
const { trackEventSpy } = bindInternalEventDocument(wrapper.element);
|
||||
findGroup().vm.$emit('action', { text: eventTrigger });
|
||||
|
||||
expect(trackEventSpy).toHaveBeenCalledWith(event, {}, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ RSpec.describe MarkupHelper, feature_category: :team_planning do
|
|||
|
||||
describe 'inside a project' do
|
||||
it 'renders uploads relative to project' do
|
||||
expect(subject).to include("#{project.full_path}/uploads/test.png")
|
||||
expect(subject).to include("/-/project/#{project.id}/uploads/test.png")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ RSpec.describe MarkupHelper, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
it 'renders uploads relative to the group' do
|
||||
expect(subject).to include("#{group.full_path}/-/uploads/test.png")
|
||||
expect(subject).to include("/-/group/#{group.id}/uploads/test.png")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ RSpec.describe MarkupHelper, feature_category: :team_planning do
|
|||
end
|
||||
|
||||
it 'renders uploads relative to project' do
|
||||
expect(subject).to include("#{project_in_group.path_with_namespace}/uploads/test.png")
|
||||
expect(subject).to include("/-/project/#{project_in_group.id}/uploads/test.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -373,7 +373,7 @@ RSpec.describe MarkupHelper, feature_category: :team_planning do
|
|||
it 'renders uploads relative to project' do
|
||||
result = helper.render_wiki_content(wiki_page)
|
||||
|
||||
expect(result).to include("#{project.full_path}#{upload_link}")
|
||||
expect(result).to include("/-/project/#{project.id}#{upload_link}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ RSpec.describe Banzai::Filter::UploadLinkFilter, feature_category: :team_plannin
|
|||
let(:project_path) { project.full_path }
|
||||
let(:only_path) { true }
|
||||
let(:upload_path) { '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' }
|
||||
let(:relative_path) { "/#{project.full_path}#{upload_path}" }
|
||||
let(:relative_path) { "/-/project/#{project.id}#{upload_path}" }
|
||||
|
||||
it 'preserves original url in data-canonical-src attribute' do
|
||||
doc = filter(link(upload_path))
|
||||
|
|
@ -102,7 +102,7 @@ RSpec.describe Banzai::Filter::UploadLinkFilter, feature_category: :team_plannin
|
|||
path = '/uploads/한글.png'
|
||||
doc = filter(link(path))
|
||||
|
||||
expect(doc.at_css('a')['href']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png")
|
||||
expect(doc.at_css('a')['href']).to eq("/-/project/#{project.id}/uploads/%ED%95%9C%EA%B8%80.png")
|
||||
expect(doc.at_css('a').classes).to include('gfm')
|
||||
expect(doc.at_css('a')['data-link']).to eq('true')
|
||||
end
|
||||
|
|
@ -112,10 +112,32 @@ RSpec.describe Banzai::Filter::UploadLinkFilter, feature_category: :team_plannin
|
|||
escaped = Addressable::URI.escape(path)
|
||||
doc = filter(image(escaped))
|
||||
|
||||
expect(doc.at_css('img')['src']).to eq("/#{project.full_path}/uploads/%ED%95%9C%EA%B8%80.png")
|
||||
expect(doc.at_css('img')['src']).to eq("/-/project/#{project.id}/uploads/%ED%95%9C%EA%B8%80.png")
|
||||
expect(doc.at_css('img').classes).to include('gfm')
|
||||
expect(doc.at_css('img')['data-link']).not_to eq('true')
|
||||
end
|
||||
|
||||
context 'when use_ids_for_markdown_upload_urls is disabled' do
|
||||
let(:relative_path) { "/#{project.full_path}#{upload_path}" }
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_ids_for_markdown_upload_urls: false)
|
||||
end
|
||||
|
||||
it 'prepends project path to the URL' do
|
||||
doc = filter(link(upload_path))
|
||||
|
||||
expect(doc.at_css('a')['href']).to eq(relative_path)
|
||||
expect(doc.at_css('a').classes).to include('gfm')
|
||||
expect(doc.at_css('a')['data-link']).to eq('true')
|
||||
|
||||
doc = filter(nested(link(upload_path)))
|
||||
|
||||
expect(doc.at_css('a')['href']).to eq(relative_path)
|
||||
expect(doc.at_css('a').classes).to include('gfm')
|
||||
expect(doc.at_css('a')['data-link']).to eq('true')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'to a group upload' do
|
||||
|
|
@ -123,7 +145,7 @@ RSpec.describe Banzai::Filter::UploadLinkFilter, feature_category: :team_plannin
|
|||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let(:project) { nil }
|
||||
let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
|
||||
let(:relative_path) { "/-/group/#{group.id}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
|
||||
|
||||
context 'with an absolute URL' do
|
||||
let(:absolute_path) { Gitlab.config.gitlab.url + relative_path }
|
||||
|
|
@ -163,6 +185,22 @@ RSpec.describe Banzai::Filter::UploadLinkFilter, feature_category: :team_plannin
|
|||
expect(doc.at_css('a').classes).not_to include('gfm')
|
||||
expect(doc.at_css('a')['data-link']).not_to eq('true')
|
||||
end
|
||||
|
||||
context 'when use_ids_for_markdown_upload_urls is disabled' do
|
||||
let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_ids_for_markdown_upload_urls: false)
|
||||
end
|
||||
|
||||
it 'prepends group path to the URL' do
|
||||
doc = filter(upload_link)
|
||||
|
||||
expect(doc.at_css('a')['href']).to eq(relative_path)
|
||||
expect(doc.at_css('a').classes).to include('gfm')
|
||||
expect(doc.at_css('a')['data-link']).to eq('true')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'to a personal snippet' do
|
||||
|
|
|
|||
|
|
@ -20,10 +20,11 @@ RSpec.describe 'CI YML Templates' do
|
|||
|
||||
context 'that support autodevops' do
|
||||
exceptions = [
|
||||
'Diffblue-Cover.gitlab-ci.yml', # no auto-devops
|
||||
'Security/DAST.gitlab-ci.yml', # DAST stage is defined inside AutoDevops yml
|
||||
'Security/DAST-API.gitlab-ci.yml', # no auto-devops
|
||||
'Security/API-Fuzzing.gitlab-ci.yml', # no auto-devops
|
||||
'Diffblue-Cover.gitlab-ci.yml', # no auto-devops
|
||||
'Security/DAST.gitlab-ci.yml', # DAST stage is defined inside AutoDevops yml
|
||||
'Security/DAST-API.gitlab-ci.yml', # no auto-devops
|
||||
'Security/API-Fuzzing.gitlab-ci.yml', # no auto-devops
|
||||
'Security/API-Security.gitlab-ci.yml', # no auto-decops
|
||||
'ThemeKit.gitlab-ci.yml'
|
||||
]
|
||||
|
||||
|
|
@ -165,6 +166,51 @@ RSpec.describe 'CI YML Templates' do
|
|||
|
||||
include_examples 'require default stages to be included'
|
||||
end
|
||||
|
||||
context 'when API Security template' do
|
||||
# The API Security template purposly excludes a stages
|
||||
# definition.
|
||||
|
||||
let(:template_name) { 'Security/API-Security.gitlab-ci.yml' }
|
||||
|
||||
context 'with default stages' do
|
||||
let(:content) do
|
||||
<<~EOS
|
||||
include:
|
||||
- template: #{template_name}
|
||||
|
||||
concrete_build_implemented_by_a_user:
|
||||
stage: test
|
||||
script: do something
|
||||
EOS
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'with defined stages' do
|
||||
let(:content) do
|
||||
<<~EOS
|
||||
include:
|
||||
- template: #{template_name}
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- dast
|
||||
|
||||
concrete_build_implemented_by_a_user:
|
||||
stage: test
|
||||
script: do something
|
||||
EOS
|
||||
end
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
include_examples 'require default stages to be included'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
|
|||
it 'has event definitions for all events used in Internal Events metric definitions', :aggregate_failures do
|
||||
from_metric_definitions = Gitlab::Usage::MetricDefinition.not_removed
|
||||
.values
|
||||
.select { |m| m.attributes[:data_source] == 'internal_events' }
|
||||
.select(&:internal_events?)
|
||||
.flat_map { |m| m.events&.keys }
|
||||
.compact
|
||||
.uniq
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ RSpec.describe Gitlab::Usage::Metric do
|
|||
|
||||
let(:issue_count_metric_definiton) do
|
||||
double(:issue_count_metric_definiton,
|
||||
attributes.merge({ attributes: attributes })
|
||||
attributes.merge({ raw_attributes: attributes })
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ RSpec.describe Gitlab::Usage::Metric do
|
|||
let(:instrumentation_class) { "UnavailableMetric" }
|
||||
let(:issue_count_metric_definiton) do
|
||||
double(:issue_count_metric_definiton,
|
||||
attributes.merge({ attributes: attributes, instrumentation_class: instrumentation_class })
|
||||
attributes.merge({ raw_attributes: attributes, instrumentation_class: instrumentation_class })
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
|
|||
end
|
||||
|
||||
def type_cast_to_defined_type(value, metric_definition)
|
||||
case metric_definition&.attributes&.fetch(:value_type)
|
||||
case metric_definition&.value_type
|
||||
when "string"
|
||||
value.to_s
|
||||
when "number"
|
||||
|
|
@ -181,7 +181,7 @@ RSpec.describe Gitlab::Usage::ServicePingReport, :use_clean_rails_memory_store_c
|
|||
metric_definition = metric_definitions[key_path.join('.')]
|
||||
|
||||
# Skip broken metrics since they are usually overriden to return -1
|
||||
next if metric_definition&.attributes&.fetch(:status) == 'broken'
|
||||
next if metric_definition&.broken?
|
||||
|
||||
value = type_cast_to_defined_type(value, metric_definition)
|
||||
payload_value = service_ping_payload.dig(*key_path)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ RSpec.describe 'Code review events' do
|
|||
]
|
||||
|
||||
all_code_review_events = Gitlab::Usage::MetricDefinition.all.flat_map do |definition|
|
||||
next [] unless definition.attributes[:key_path].include?('.code_review.') &&
|
||||
definition.attributes[:status] == 'active' &&
|
||||
next [] unless definition.key_path.include?('.code_review.') &&
|
||||
definition.active? &&
|
||||
definition.events.count == 1
|
||||
|
||||
definition.events.keys
|
||||
|
|
@ -37,8 +37,8 @@ RSpec.describe 'Code review events' do
|
|||
end
|
||||
|
||||
def code_review_aggregated_metric?(definition)
|
||||
definition.attributes[:product_group] == 'code_review' &&
|
||||
definition.attributes[:status] == 'active' &&
|
||||
definition.product_group == 'code_review' &&
|
||||
definition.active? &&
|
||||
definition.events.count > 1
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ RSpec.describe Emails::Releases do
|
|||
let(:release) { create(:release, project: project, description: "Attachment: [Test file](#{upload_path})") }
|
||||
|
||||
it 'renders absolute links' do
|
||||
is_expected.to have_body_text(%(<a href="#{project.web_url}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">Test file</a>))
|
||||
is_expected.to have_body_text(%(<a href="#{root_url}-/project/#{project.id}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">Test file</a>))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
end
|
||||
end
|
||||
|
||||
let_it_be(:expected_html) { %(a new comment with <a href="#{project.web_url}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
let_it_be(:expected_html) { %(a new comment with <a href="#{root_url}-/project/#{project.id}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
let_it_be(:expected_template_html) { %(some text #{expected_html}) }
|
||||
|
||||
it_behaves_like 'a service desk notification email'
|
||||
|
|
@ -459,7 +459,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
context 'when not all uploads processed correct' do # rubocop:disable RSpec/MultipleMemoizedHelpers -- Avoid duplication with heavy use of helpers
|
||||
let(:attachments_count) { 1 }
|
||||
|
||||
let_it_be(:expected_html) { %(a new comment with <strong>#{filename}</strong> <a href="#{project.web_url}#{upload_path_1}" data-canonical-src="#{upload_path_1}" data-link="true" class="gfm">#{filename_1}</a>) }
|
||||
let_it_be(:expected_html) { %(a new comment with <strong>#{filename}</strong> <a href="#{root_url}-/project/#{project.id}#{upload_path_1}" data-canonical-src="#{upload_path_1}" data-link="true" class="gfm">#{filename_1}</a>) }
|
||||
let_it_be(:expected_template_html) { %(some text #{expected_html}) }
|
||||
|
||||
it_behaves_like 'a service desk notification email'
|
||||
|
|
@ -476,7 +476,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(StandardError, project_id: note.project_id)
|
||||
end
|
||||
|
||||
let_it_be(:expected_template_html) { %(some text a new comment with <a href="#{project.web_url}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
let_it_be(:expected_template_html) { %(some text a new comment with <a href="#{root_url}-/project/#{project.id}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
|
||||
it_behaves_like 'a service desk notification email with template content'
|
||||
end
|
||||
|
|
@ -489,7 +489,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do
|
|||
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(StandardError, project_id: note.project_id)
|
||||
end
|
||||
|
||||
let_it_be(:expected_template_html) { %(some text a new comment with <a href="#{project.web_url}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
let_it_be(:expected_template_html) { %(some text a new comment with <a href="#{root_url}-/project/#{project.id}#{upload_path}" data-canonical-src="#{upload_path}" data-link="true" class="gfm">#{filename}</a>) }
|
||||
|
||||
it_behaves_like 'a service desk notification email with template content'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2341,7 +2341,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
expect(json_response['alt']).to eq("dk")
|
||||
expect(json_response['url']).to start_with("/uploads/")
|
||||
expect(json_response['url']).to end_with("/dk.png")
|
||||
expect(json_response['full_path']).to start_with("/#{project.namespace.path}/#{project.path}/uploads")
|
||||
expect(json_response['full_path']).to start_with("/-/project/#{project.id}/uploads")
|
||||
end
|
||||
|
||||
it "does not leave the temporary file in place after uploading, even when the tempfile reaper does not run" do
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process', feature_category: :team
|
|||
canonical: |
|
||||
<p><a href="groups-test-file">groups-test-file</a></p>
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:45" dir="auto"><a data-sourcepos="1:1-1:45" href="/groups/glfm_group/-/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
|
||||
<p data-sourcepos="1:1-1:45" dir="auto"><a data-sourcepos="1:1-1:45" href="/-/group/66666/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
|
||||
wysiwyg: |-
|
||||
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/groups-test-file">groups-test-file</a></p>
|
||||
06_02_00__api_request_overrides__project_repo_link__001:
|
||||
|
|
|
|||
|
|
@ -360,33 +360,5 @@ RSpec.describe AutoMerge::BaseService, feature_category: :code_review_workflow d
|
|||
expect(available_for).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when refactor_auto_merge is disabled' do
|
||||
before do
|
||||
stub_feature_flags(refactor_auto_merge: false)
|
||||
allow(merge_request).to receive(:can_be_merged_by?).and_return(can_be_merged)
|
||||
allow(merge_request).to receive(:open?).and_return(open)
|
||||
allow(merge_request).to receive(:broken?).and_return(broken)
|
||||
allow(merge_request).to receive(:draft?).and_return(draft)
|
||||
allow(merge_request).to receive(:mergeable_discussions_state?).and_return(discussions)
|
||||
allow(merge_request).to receive(:merge_blocked_by_other_mrs?).and_return(blocked)
|
||||
end
|
||||
|
||||
where(:can_be_merged, :open, :broken, :discussions, :blocked, :draft, :result) do
|
||||
true | true | false | true | false | false | true
|
||||
false | true | false | true | false | false | false
|
||||
true | false | false | true | false | false | false
|
||||
true | true | true | true | false | false | false
|
||||
true | true | false | false | false | false | false
|
||||
true | true | false | true | true | false | false
|
||||
true | true | false | true | false | true | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'returns the expected results' do
|
||||
expect(available_for).to eq(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ RSpec::Matchers.define :increment_usage_metrics do |*key_paths|
|
|||
# to be passed to a change matcher
|
||||
def metric_value_tracker(key_path, metric_definition)
|
||||
proc do
|
||||
stub_usage_data_connections if metric_definition.attributes[:data_source] == 'database'
|
||||
stub_usage_data_connections if metric_definition.data_source == 'database'
|
||||
|
||||
metric = Gitlab::Usage::Metric.new(metric_definition)
|
||||
instrumentation_object = stub_metric_timeframe(metric)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ module MarkdownMatchers
|
|||
link = actual.at_css('a:contains("Relative Upload Link")')
|
||||
image = actual.at_css('img[alt="Relative Upload Image"]')
|
||||
|
||||
expect(link['href']).to eq("/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg")
|
||||
expect(image['data-src']).to eq("/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg")
|
||||
expect(link['href']).to eq("/-/project/#{project.id}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg")
|
||||
expect(image['data-src']).to eq("/-/project/#{project.id}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
RSpec.shared_context 'with GLFM example snapshot fixtures' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group, name: 'glfm_group', owners: user) }
|
||||
let_it_be(:group) { create(:group, name: 'glfm_group', owners: user, id: 66666) }
|
||||
|
||||
let_it_be(:project) do
|
||||
# NOTE: We hardcode the IDs on all fixtures to prevent variability in the
|
||||
|
|
|
|||
Loading…
Reference in New Issue