Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7f5c968c6e
commit
1cbefa896f
|
@ -646,10 +646,13 @@ lib/gitlab/checks/**
|
|||
/doc/administration/reference_architectures/ @axil
|
||||
/doc/administration/reply_by_email.md @lciutacu
|
||||
/doc/administration/reply_by_email_postfix_setup.md @axil
|
||||
/doc/administration/reporting/ @axil
|
||||
/doc/administration/reporting/ @idurham
|
||||
/doc/administration/reporting/spamcheck.md @axil
|
||||
/doc/administration/repository_checks.md @eread
|
||||
/doc/administration/repository_storage_paths.md @eread
|
||||
/doc/administration/restart_gitlab.md @axil
|
||||
/doc/administration/review_abuse_reports.md @idurham
|
||||
/doc/administration/review_spam_logs.md @idurham
|
||||
/doc/administration/self_hosted_models/ @jglassman1
|
||||
/doc/administration/server_hooks.md @eread
|
||||
/doc/administration/settings/account_and_limit_settings.md @brendan777
|
||||
|
@ -965,6 +968,7 @@ lib/gitlab/checks/**
|
|||
/doc/development/git_object_deduplication.md @proglottis @toon
|
||||
/doc/development/gitaly.md @proglottis @toon
|
||||
/doc/development/gitpod_internals.md @gl-dx/eng-prod
|
||||
/doc/development/identity_verification.md @gitlab-org/software-supply-chain-security/authorization/approvers
|
||||
/doc/development/image_scaling.md @abdwdd @alexpooley
|
||||
/doc/development/internal_analytics/ @gitlab-org/analytics-section/product-analytics/engineers/frontend @gitlab-org/analytics-section/analytics-instrumentation/engineers
|
||||
/doc/development/internal_analytics/product_analytics.md @gitlab-org/analytics-section/product-analytics/engineers/frontend
|
||||
|
@ -975,14 +979,15 @@ lib/gitlab/checks/**
|
|||
/doc/development/observability/ @gitlab-org/analytics-section/product-analytics/engineers/frontend
|
||||
/doc/development/omnibus.md @gitlab-org/distribution
|
||||
/doc/development/organization/ @abdwdd @alexpooley
|
||||
/doc/development/permissions.md @gitlab-org/govern/authorization/approvers
|
||||
/doc/development/permissions/ @gitlab-org/govern/authorization/approvers
|
||||
/doc/development/permissions.md @gitlab-org/software-supply-chain-security/authorization/approvers
|
||||
/doc/development/permissions/ @gitlab-org/software-supply-chain-security/authorization/approvers
|
||||
/doc/development/pipelines/ @gl-dx/eng-prod
|
||||
/doc/development/policies.md @gitlab-org/govern/authentication/approvers
|
||||
/doc/development/policies.md @gitlab-org/software-supply-chain-security/authentication/approvers
|
||||
/doc/development/prometheus_metrics.md @gitlab-org/analytics-section/product-analytics/engineers/frontend
|
||||
/doc/development/search/ @gitlab-org/search-team/migration-maintainers
|
||||
/doc/development/sec/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/static-analysis
|
||||
/doc/development/software_design.md @gl-dx/eng-prod
|
||||
/doc/development/spam_protection_and_captcha/ @gitlab-org/software-supply-chain-security/authorization/approvers
|
||||
/doc/development/stage_group_observability/ @gitlab-org/analytics-section/product-analytics/engineers/frontend
|
||||
/doc/development/tracing.md @gitlab-org/analytics-section/product-analytics/engineers/frontend
|
||||
/doc/downgrade_ee_to_ce/ @axil
|
||||
|
@ -1077,8 +1082,8 @@ lib/gitlab/checks/**
|
|||
/doc/user/compliance/license_approval_policies.md @rlehmann1
|
||||
/doc/user/compliance/license_scanning_of_cyclonedx_files/ @rdickenson
|
||||
/doc/user/crm/ @msedlakjakubowski
|
||||
/doc/user/custom_roles.md @rlehmann1
|
||||
/doc/user/custom_roles/ @rlehmann1
|
||||
/doc/user/custom_roles.md @idurham
|
||||
/doc/user/custom_roles/ @idurham
|
||||
/doc/user/discussions/ @aqualls
|
||||
/doc/user/duo_amazon_q/ @sselhorn
|
||||
/doc/user/duo_workflow/ @sselhorn
|
||||
|
@ -1107,6 +1112,8 @@ lib/gitlab/checks/**
|
|||
/doc/user/group/issues_analytics/ @lciutacu
|
||||
/doc/user/group/iterations/ @msedlakjakubowski
|
||||
/doc/user/group/manage.md @emily.sahlani
|
||||
/doc/user/group/moderate_users.md @idurham
|
||||
/doc/user/group/reporting/ @idurham
|
||||
/doc/user/group/repositories_analytics/ @lyspin
|
||||
/doc/user/group/roadmap/ @msedlakjakubowski
|
||||
/doc/user/group/saml_sso/ @idurham
|
||||
|
@ -1128,7 +1135,7 @@ lib/gitlab/checks/**
|
|||
/doc/user/packages/container_registry/ @lyspin
|
||||
/doc/user/packages/dependency_proxy/ @lyspin
|
||||
/doc/user/packages/harbor_container_registry/ @lyspin
|
||||
/doc/user/permissions.md @rlehmann1
|
||||
/doc/user/permissions.md @idurham
|
||||
/doc/user/profile/account/ @idurham
|
||||
/doc/user/profile/account/create_accounts.md @lciutacu
|
||||
/doc/user/profile/achievements.md @emily.sahlani
|
||||
|
@ -1204,6 +1211,7 @@ lib/gitlab/checks/**
|
|||
/doc/user/project/wiki/ @msedlakjakubowski
|
||||
/doc/user/project/working_with_projects.md @emily.sahlani
|
||||
/doc/user/public_access.md @emily.sahlani
|
||||
/doc/user/report_abuse.md @idurham
|
||||
/doc/user/reserved_names.md @emily.sahlani
|
||||
/doc/user/rich_text_editor.md @msedlakjakubowski
|
||||
/doc/user/search/ @ashrafkhamis
|
||||
|
|
|
@ -1 +1 @@
|
|||
f1a54081b5323ecbb0f360e8d479e7878a87138f
|
||||
1ea2d19a4cd8e6f4bfdd3fbde0a82f8b129e9e40
|
||||
|
|
|
@ -66,14 +66,14 @@ export default {
|
|||
<span class="gl-font-bold gl-text-subtle">{{ diffsCount }} {{ filesText }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group gl-flex gl-items-center gl-text-green-600"
|
||||
class="diff-stats-group gl-flex gl-items-center gl-text-success"
|
||||
:class="{ 'gl-font-bold': isCompareVersionsHeader }"
|
||||
>
|
||||
<span>+</span>
|
||||
<span data-testid="js-file-addition-line">{{ addedLines }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="diff-stats-group gl-flex gl-items-center gl-text-red-500"
|
||||
class="diff-stats-group gl-flex gl-items-center gl-text-danger"
|
||||
:class="{ 'gl-font-bold': isCompareVersionsHeader }"
|
||||
>
|
||||
<span>−</span>
|
||||
|
|
|
@ -11,8 +11,8 @@ export default {
|
|||
|
||||
<template>
|
||||
<span class="file-row-stats">
|
||||
<span data-testid="file-added-lines" class="gl-text-green-600"> +{{ file.addedLines }} </span>
|
||||
<span data-testid="file-removed-lines" class="gl-text-red-500"> -{{ file.removedLines }} </span>
|
||||
<span data-testid="file-added-lines" class="gl-text-success"> +{{ file.addedLines }} </span>
|
||||
<span data-testid="file-removed-lines" class="gl-text-danger"> -{{ file.removedLines }} </span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -136,9 +136,9 @@ export function stats(file) {
|
|||
valid = true;
|
||||
|
||||
if (diff > 0) {
|
||||
classes = 'gl-text-green-600';
|
||||
classes = 'gl-text-success';
|
||||
} else if (diff < 0) {
|
||||
classes = 'gl-text-red-500';
|
||||
classes = 'gl-text-danger';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,13 +95,13 @@ export default (elements, issuablePopoverMount = handleIssuablePopoverMount) =>
|
|||
const listenerAddedAttr = 'data-popover-listener-added';
|
||||
|
||||
elements.forEach((el) => {
|
||||
const { projectPath, groupPath, iid, referenceType, milestone, project } = el.dataset;
|
||||
const { projectPath, groupPath, iid, referenceType, milestone } = el.dataset;
|
||||
let { namespacePath } = el.dataset;
|
||||
const title = el.dataset.mrTitle || el.title;
|
||||
const { innerText } = el;
|
||||
namespacePath = namespacePath || groupPath || projectPath;
|
||||
const isIssuable = Boolean(namespacePath && title && iid);
|
||||
const isMilestone = Boolean(milestone && project);
|
||||
const isMilestone = Boolean(milestone);
|
||||
|
||||
if (!el.getAttribute(listenerAddedAttr) && referenceType && (isIssuable || isMilestone)) {
|
||||
el.addEventListener('mouseenter', ({ target }) => {
|
||||
|
|
|
@ -46,13 +46,24 @@ export default {
|
|||
|
||||
axios
|
||||
.post(this.reassignmentCsvPath, formData)
|
||||
.then(() => {
|
||||
// nothing to do here, modal already closes itself
|
||||
})
|
||||
.catch(() => {
|
||||
.then((response) => {
|
||||
createAlert({
|
||||
message: s__('UserMapping|Something went wrong while uploading the CSV file.'),
|
||||
message: response.data.message,
|
||||
variant: 'success',
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response.data?.message) {
|
||||
const { message } = error.response.data;
|
||||
|
||||
createAlert({
|
||||
message,
|
||||
});
|
||||
} else {
|
||||
createAlert({
|
||||
message: s__('UserMapping|Something went wrong while uploading the CSV file.'),
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
isValidFileType(file) {
|
||||
|
@ -119,8 +130,8 @@ export default {
|
|||
icon="download"
|
||||
data-testid="csv-download-button"
|
||||
class="vertical-align-text-top"
|
||||
>{{ s__('UserMapping|Download the prefilled CSV template.') }}</gl-button
|
||||
>
|
||||
>{{ s__('UserMapping|Download the prefilled CSV template.') }}
|
||||
</gl-button>
|
||||
</li>
|
||||
<li>{{ s__('UserMapping|Review and complete the CSV file.') }}</li>
|
||||
<li>{{ s__('UserMapping|Upload the completed CSV file.') }}</li>
|
||||
|
|
|
@ -72,10 +72,11 @@ export default {
|
|||
},
|
||||
isMergeRequestBroken() {
|
||||
return (
|
||||
this.mergeRequest.commitCount === 0 ||
|
||||
!this.mergeRequest.sourceBranchExists ||
|
||||
!this.mergeRequest.targetBranchExists ||
|
||||
this.mergeRequest.conflicts
|
||||
this.mergeRequest.state === 'opened' &&
|
||||
(this.mergeRequest.commitCount === 0 ||
|
||||
!this.mergeRequest.sourceBranchExists ||
|
||||
!this.mergeRequest.targetBranchExists ||
|
||||
this.mergeRequest.conflicts)
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -177,6 +178,7 @@ export default {
|
|||
name="warning-solid"
|
||||
variant="subtle"
|
||||
class="gl-mt-1"
|
||||
data-testid="mr-broken-badge"
|
||||
/>
|
||||
<discussions-badge
|
||||
v-if="mergeRequest.resolvableDiscussionsCount"
|
||||
|
@ -204,11 +206,11 @@ export default {
|
|||
<gl-icon name="doc-code" />
|
||||
<span>{{ mergeRequest.diffStatsSummary.fileCount }}</span>
|
||||
</div>
|
||||
<div class="gl-flex gl-items-center gl-font-bold gl-text-green-600">
|
||||
<div class="gl-flex gl-items-center gl-font-bold gl-text-success">
|
||||
<span>+</span>
|
||||
<span>{{ mergeRequest.diffStatsSummary.additions }}</span>
|
||||
</div>
|
||||
<div class="gl-flex gl-items-center gl-font-bold gl-text-red-500">
|
||||
<div class="gl-flex gl-items-center gl-font-bold gl-text-danger">
|
||||
<span>−</span>
|
||||
<span>{{ mergeRequest.diffStatsSummary.deletions }}</span>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ fragment MergeRequestDashboardFragment on MergeRequest {
|
|||
title
|
||||
webUrl
|
||||
draft
|
||||
state
|
||||
author {
|
||||
...User
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ export async function mountMergeRequestListsApp({
|
|||
newMergeRequestPath,
|
||||
showExportButton,
|
||||
issuableType,
|
||||
issuableCount,
|
||||
email,
|
||||
exportCsvPath,
|
||||
rssUrl,
|
||||
|
@ -71,7 +70,6 @@ export async function mountMergeRequestListsApp({
|
|||
newMergeRequestPath,
|
||||
showExportButton: parseBoolean(showExportButton),
|
||||
issuableType,
|
||||
issuableCount: Number(issuableCount),
|
||||
email,
|
||||
exportCsvPath,
|
||||
rssUrl,
|
||||
|
|
|
@ -6,6 +6,8 @@ import { s__, sprintf } from '~/locale';
|
|||
import { nHoursAfter } from '~/lib/utils/datetime_utility';
|
||||
import { reportToSentry } from '~/ci/utils';
|
||||
import { localeDateFormat } from '~/lib/utils/datetime/locale_dateformat';
|
||||
import Tracking from '~/tracking';
|
||||
import { INSTRUMENT_TODO_ITEM_CLICK } from '~/todos/constants';
|
||||
import snoozeTodoMutation from './mutations/snooze_todo.mutation.graphql';
|
||||
import unSnoozeTodoMutation from './mutations/un_snooze_todo.mutation.graphql';
|
||||
|
||||
|
@ -15,6 +17,7 @@ export default {
|
|||
GlDisclosureDropdown,
|
||||
GlTooltip,
|
||||
},
|
||||
mixins: [Tracking.mixin()],
|
||||
inject: ['currentTime'],
|
||||
props: {
|
||||
todo: {
|
||||
|
@ -62,7 +65,12 @@ export default {
|
|||
day: dateFormat(forAnHour, 'DDDD'),
|
||||
time: toTimeString(forAnHour),
|
||||
}),
|
||||
action: () => this.snooze(forAnHour),
|
||||
action: () => {
|
||||
this.track(INSTRUMENT_TODO_ITEM_CLICK, {
|
||||
label: 'snooze_for_one_hour',
|
||||
});
|
||||
this.snooze(forAnHour);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: s__('Todos|Until later today'),
|
||||
|
@ -70,7 +78,12 @@ export default {
|
|||
day: dateFormat(untilLaterToday, 'DDDD'),
|
||||
time: toTimeString(untilLaterToday),
|
||||
}),
|
||||
action: () => this.snooze(untilLaterToday),
|
||||
action: () => {
|
||||
this.track(INSTRUMENT_TODO_ITEM_CLICK, {
|
||||
label: 'snooze_until_later_today',
|
||||
});
|
||||
this.snooze(untilLaterToday);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: s__('Todos|Until tomorrow'),
|
||||
|
@ -79,6 +92,10 @@ export default {
|
|||
time: toTimeString(untilTomorrow),
|
||||
}),
|
||||
action: () => {
|
||||
this.track(INSTRUMENT_TODO_ITEM_CLICK, {
|
||||
label: 'snooze_until_tomorrow',
|
||||
});
|
||||
|
||||
this.snooze(untilTomorrow);
|
||||
},
|
||||
},
|
||||
|
@ -119,6 +136,9 @@ export default {
|
|||
}
|
||||
},
|
||||
async unSnooze() {
|
||||
this.track(INSTRUMENT_TODO_ITEM_CLICK, {
|
||||
label: 'remove_snooze',
|
||||
});
|
||||
try {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: unSnoozeTodoMutation,
|
||||
|
|
|
@ -12,7 +12,7 @@ export const i18n = {
|
|||
|
||||
const variantCssColorMap = {
|
||||
success: 'gl-text-success',
|
||||
danger: 'gl-text-red-500',
|
||||
danger: 'gl-text-danger',
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -128,8 +128,8 @@ export default {
|
|||
>{{ item.text }}</span
|
||||
>
|
||||
<span class="gl-ml-auto gl-whitespace-nowrap" aria-hidden="true">
|
||||
<span class="gl-text-green-600">+{{ item.added }}</span>
|
||||
<span class="gl-text-red-500">-{{ item.removed }}</span>
|
||||
<span class="gl-text-success">+{{ item.added }}</span>
|
||||
<span class="gl-text-danger">-{{ item.removed }}</span>
|
||||
</span>
|
||||
<span class="gl-sr-only"
|
||||
>{{ additionsText(item.added) }}, {{ deletionsText(item.removed) }}</span
|
||||
|
@ -150,10 +150,10 @@ export default {
|
|||
>
|
||||
<gl-sprintf :message="$options.i18n.messageAdditionsDeletions">
|
||||
<template #additions>
|
||||
<span class="gl-font-bold gl-text-green-600">{{ additionsText() }}</span>
|
||||
<span class="gl-font-bold gl-text-success">{{ additionsText() }}</span>
|
||||
</template>
|
||||
<template #deletions>
|
||||
<span class="gl-font-bold gl-text-red-500">{{ deletionsText() }}</span>
|
||||
<span class="gl-font-bold gl-text-danger">{{ deletionsText() }}</span>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
|
|
|
@ -103,10 +103,10 @@ export default {
|
|||
</span>
|
||||
<span v-if="file.changed || file.tempFile" v-once class="diff-changed-stats">
|
||||
<span v-if="showDiffStats">
|
||||
<span class="gl-font-bold gl-text-green-600">
|
||||
<span class="gl-font-bold gl-text-success">
|
||||
<gl-icon name="file-addition" class="align-text-top" /> {{ file.addedLines }}
|
||||
</span>
|
||||
<span class="ml-1 gl-font-bold gl-text-red-500">
|
||||
<span class="ml-1 gl-font-bold gl-text-danger">
|
||||
<gl-icon name="file-deletion" class="align-text-top" /> {{ file.removedLines }}
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
@ -251,7 +251,6 @@ export default {
|
|||
this.$emit('input', target.value);
|
||||
|
||||
this.saveDraft();
|
||||
this.autosizeTextarea();
|
||||
},
|
||||
renderMarkdown(markdown) {
|
||||
const url = setUrlParams(
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Groups
|
||||
class BulkPlaceholderAssignmentsController < Groups::ApplicationController
|
||||
include WorkhorseAuthorization
|
||||
|
||||
PERMITTED_FILE_EXTENSIONS = %w[csv].freeze
|
||||
|
||||
before_action :authorize_owner_access!
|
||||
|
||||
feature_category :importers
|
||||
|
||||
def show
|
||||
return render_404 unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
|
||||
|
||||
csv_response = Import::SourceUsers::GenerateCsvService.new(group, current_user: current_user).execute
|
||||
|
||||
if csv_response.success?
|
||||
send_data(
|
||||
csv_response.payload,
|
||||
filename: "bulk_reassignments_for_namespace_#{group.id}_#{Time.current.to_i}.csv",
|
||||
type: 'text/csv; charset=utf-8'
|
||||
)
|
||||
else
|
||||
redirect_back_or_default(options: { alert: csv_response.message })
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
return render_404 unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
|
||||
|
||||
unless file_is_valid?(file_params[:file])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: { message: s_('UserMapping|You must upload a CSV file with a .csv file extension.') },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
message: s_('UserMapping|The file is being processed and you will receive an email when completed.')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def file_params
|
||||
params.permit(:file)
|
||||
end
|
||||
|
||||
def uploader_class
|
||||
FileUploader
|
||||
end
|
||||
|
||||
def file_extension_allowlist
|
||||
PERMITTED_FILE_EXTENSIONS
|
||||
end
|
||||
|
||||
def maximum_size
|
||||
Gitlab::CurrentSettings.max_attachment_size.megabytes
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
# Authorize
|
||||
before_action :authorize_owner_access!, only: :bulk_reassignment_file
|
||||
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
|
||||
before_action :authorize_read_group_member!, only: :index
|
||||
|
||||
|
@ -52,22 +51,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
def bulk_reassignment_file
|
||||
return render_404 unless Feature.enabled?(:importer_user_mapping_reassignment_csv, current_user)
|
||||
|
||||
csv_response = Import::SourceUsers::GenerateCsvService.new(membershipable, current_user: current_user).execute
|
||||
|
||||
if csv_response.success?
|
||||
send_data(
|
||||
csv_response.payload,
|
||||
filename: "bulk_reassignments_for_namespace_#{membershipable.id}_#{Time.current.to_i}.csv",
|
||||
type: 'text/csv; charset=utf-8'
|
||||
)
|
||||
else
|
||||
redirect_back_or_default(options: { alert: csv_response.message })
|
||||
end
|
||||
end
|
||||
|
||||
# MembershipActions concern
|
||||
alias_method :membershipable, :group
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ module Groups::GroupMembersHelper
|
|||
can_approve_access_requests: true, # true for CE, overridden in EE
|
||||
placeholder: placeholder_users,
|
||||
available_roles: available_group_roles(group),
|
||||
reassignment_csv_path: bulk_reassignment_file_group_group_members_path(group)
|
||||
reassignment_csv_path: group_bulk_reassignment_file_path(group)
|
||||
}
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
|
|
@ -262,7 +262,6 @@ module MergeRequestsHelper
|
|||
is_signed_in: current_user.present?.to_s,
|
||||
show_export_button: "true",
|
||||
issuable_type: :merge_request,
|
||||
issuable_count: issuables_count_for_state(:merge_request, params[:state]),
|
||||
email: current_user.present? ? current_user.notification_email_or_default : nil,
|
||||
rss_url: url_for(safe_params.merge(rss_url_options)),
|
||||
emails_help_page_path: help_page_path('development/emails.md', anchor: 'email-namespace'),
|
||||
|
|
|
@ -93,6 +93,7 @@ module ApplicationSettingImplementation
|
|||
external_pipeline_validation_service_token: nil,
|
||||
external_pipeline_validation_service_url: nil,
|
||||
failed_login_attempts_unlock_period_in_minutes: nil,
|
||||
fetch_observability_alerts_from_cloud: true,
|
||||
first_day_of_week: 0,
|
||||
floc_enabled: false,
|
||||
gitaly_timeout_default: 55,
|
||||
|
|
|
@ -11,19 +11,25 @@ module Integrations
|
|||
|
||||
field :recipients,
|
||||
type: :textarea,
|
||||
help: -> { _('Comma-separated list of email addresses.') },
|
||||
help: -> { _('Comma-separated list of recipient email addresses.') },
|
||||
required: true
|
||||
|
||||
field :notify_only_broken_pipelines,
|
||||
type: :checkbox
|
||||
type: :checkbox,
|
||||
description: -> { _('Send notifications for broken pipelines.') }
|
||||
|
||||
field :notify_only_default_branch,
|
||||
type: :checkbox,
|
||||
api_only: true
|
||||
api_only: true,
|
||||
description: -> { _('Send notifications for the default branch.') }
|
||||
|
||||
field :branches_to_be_notified,
|
||||
type: :select,
|
||||
title: -> { s_('Integrations|Branches for which notifications are to be sent') },
|
||||
description: -> {
|
||||
_('Branches to send notifications for. Valid options are `all`, `default`, `protected`, ' \
|
||||
'and `default_and_protected`. The default value is `default`.')
|
||||
},
|
||||
choices: branch_choices
|
||||
|
||||
def initialize_properties
|
||||
|
|
|
@ -5,7 +5,7 @@ class WorkItem < Issue
|
|||
|
||||
COMMON_QUICK_ACTIONS_COMMANDS = [
|
||||
:title, :reopen, :close, :cc, :tableflip, :shrug, :type, :promote_to, :checkin_reminder,
|
||||
:subscribe, :unsubscribe, :confidential, :award, :react
|
||||
:subscribe, :unsubscribe, :confidential, :award, :react, :move, :clone
|
||||
].freeze
|
||||
|
||||
self.table_name = 'issues'
|
||||
|
|
|
@ -63,8 +63,11 @@ module WorkItems
|
|||
return unless work_item_parent && work_item
|
||||
|
||||
if work_item_parent.confidential? && !work_item.confidential?
|
||||
errors.add :work_item, _("cannot assign a non-confidential work item to a confidential "\
|
||||
"parent. Make the work item confidential and try again.")
|
||||
errors.add :work_item, format(
|
||||
_("cannot assign a non-confidential %{work_item_type} to a confidential "\
|
||||
"parent. Make the %{work_item_type} confidential and try again."),
|
||||
work_item_type: work_item.work_item_type.name.downcase
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module WorkItems
|
|||
link.move_to_start
|
||||
end
|
||||
|
||||
create_notes_and_resource_event(work_item, link) if link.changed? && link.save
|
||||
create_notes_and_resource_event(work_item, link) if link.save
|
||||
|
||||
link
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
|
||||
add_page_specific_style 'page_bundles/issuable_list'
|
||||
|
||||
- new_lists_enabled = ::Feature.enabled?(:merge_request_dashboard_new_lists, current_user, type: :wip)
|
||||
= render_dashboard_ultimate_trial(current_user)
|
||||
= render_if_exists 'shared/dashboard/saml_reauth_notice',
|
||||
groups_requiring_saml_reauth: user_groups_requiring_reauth
|
||||
|
@ -26,14 +27,14 @@
|
|||
= render 'shared/new_project_item_vue_select'
|
||||
|
||||
- if merge_request_dashboard_enabled?(current_user) && !current_page?(merge_requests_search_dashboard_path)
|
||||
#js-merge-request-dashboard{ data: { base_path: merge_requests_dashboard_path, new_lists_enabled: ::Feature.enabled?(:merge_request_dashboard_new_lists, current_user, type: :wip).to_s, merge_requests_search_dashboard_path: merge_requests_search_dashboard_path(assignee_username: current_user.username), initial_data: merge_request_dashboard_data.to_json } }
|
||||
#js-merge-request-dashboard{ data: { base_path: merge_requests_dashboard_path, new_lists_enabled: new_lists_enabled.to_s, merge_requests_search_dashboard_path: merge_requests_search_dashboard_path(assignee_username: current_user.username), initial_data: merge_request_dashboard_data.to_json } }
|
||||
= gl_loading_icon(size: 'lg')
|
||||
|
||||
- if !merge_request_dashboard_enabled?(current_user) || current_page?(merge_requests_search_dashboard_path)
|
||||
- if merge_request_dashboard_enabled?(current_user)
|
||||
= gl_tabs_nav do
|
||||
= gl_tab_link_to _('Needs attention'), merge_requests_dashboard_path
|
||||
= gl_tab_link_to _('Following'), merge_requests_following_dashboard_path
|
||||
= gl_tab_link_to new_lists_enabled ? _('Active') : _('Needs attention'), merge_requests_dashboard_path
|
||||
= gl_tab_link_to new_lists_enabled ? _('Merged') : _('Following'), merge_requests_following_dashboard_path
|
||||
= gl_tab_link_to _('Search'), merge_requests_search_dashboard_path, item_active: true
|
||||
|
||||
%div{ class: "#{'gl-bg-gray-10' if merge_request_dashboard_enabled?(current_user)}" }
|
||||
|
|
|
@ -48,9 +48,9 @@
|
|||
pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS,
|
||||
required: true,
|
||||
title: _('Please create a username with only alphanumeric characters.')
|
||||
%p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide
|
||||
%p.validation-error.gl-text-danger.gl-field-error-ignore.gl-mt-2.field-validation.hide
|
||||
= _('Username is already taken.')
|
||||
%p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide
|
||||
%p.validation-success.gl-text-success.gl-field-error-ignore.gl-mt-2.field-validation.hide
|
||||
= _('Username is available.')
|
||||
%p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide
|
||||
= _('Checking username availability...')
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
- @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.licensed_feature_available?(:group_bulk_edit) && issuables_count_for_state(:merge_requests, :all) > 0
|
||||
|
||||
- has_bulk_update_permission = can?(current_user, :admin_merge_request, @group) && @group.licensed_feature_available?(:group_bulk_edit)
|
||||
- page_title _("Merge requests")
|
||||
- add_page_specific_style 'page_bundles/issuable_list'
|
||||
|
||||
- if Feature.enabled?(:vue_merge_request_list, @group)
|
||||
.js-merge-request-list-root{ data: group_merge_requests_list_data(@group, current_user) }
|
||||
- if @can_bulk_update
|
||||
- if has_bulk_update_permission
|
||||
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :merge_requests
|
||||
- else
|
||||
- can_bulk_update = has_bulk_update_permission && issuables_count_for_state(:merge_requests, :all) > 0
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@search_timeout_occurred
|
||||
- if current_user
|
||||
.nav-controls
|
||||
- if @can_bulk_update
|
||||
- if can_bulk_update
|
||||
= render_if_exists 'projects/merge_requests/bulk_update_button'
|
||||
|
||||
= render 'shared/new_project_item_vue_select'
|
||||
|
||||
= render 'shared/issuable/search_bar', type: :merge_requests
|
||||
- if @can_bulk_update
|
||||
- if can_bulk_update
|
||||
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :merge_requests
|
||||
|
||||
- if @search_timeout_occurred
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
api_type:
|
||||
attr: observability_settings
|
||||
clusterwide: false
|
||||
column: observability_settings
|
||||
db_type: jsonb
|
||||
default: "'{}'::jsonb"
|
||||
description: Contains application settings for Observability features
|
||||
encrypted: false
|
||||
gitlab_com_different_than_default: false
|
||||
jihu: false
|
||||
not_null: true
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: go_get_handle_relative_url
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/508593
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176097
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/510100
|
||||
milestone: '17.8'
|
||||
group: group::source code
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: ci_secure_files_read_only
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84089
|
||||
rollout_issue_url:
|
||||
milestone: '14.10'
|
||||
type: ops
|
||||
group: group::incubation
|
||||
default_enabled: false
|
|
@ -120,7 +120,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
|
|||
post :resend_invite, on: :member
|
||||
|
||||
collection do
|
||||
get :bulk_reassignment_file
|
||||
resource :bulk_reassignment_file, only: %i[show create], controller: 'bulk_placeholder_assignments' do
|
||||
post :authorize
|
||||
end
|
||||
|
||||
delete :leave
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddO11ySettingsToApplicationSettings < Gitlab::Database::Migration[2.2]
|
||||
enable_lock_retries!
|
||||
milestone '17.8'
|
||||
|
||||
def change
|
||||
add_column :application_settings, :observability_settings, :jsonb, default: {}, null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddO11ySettingsHashConstraint < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
milestone '17.8'
|
||||
|
||||
CONSTRAINT_NAME = 'check_application_settings_o11y_settings_is_hash'
|
||||
|
||||
def up
|
||||
add_check_constraint(
|
||||
:application_settings,
|
||||
"(jsonb_typeof(observability_settings) = 'object')",
|
||||
CONSTRAINT_NAME
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_check_constraint :application_settings, CONSTRAINT_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
ec474fca5d3e528477f838c937960469619ffc22739cca03b3afb9517a950989
|
|
@ -0,0 +1 @@
|
|||
06234102c3cab9b43a17fcbf3c8a9772174efa97322a3de9beb2b2b6720f3003
|
|
@ -7452,6 +7452,7 @@ CREATE TABLE application_settings (
|
|||
elasticsearch_indexed_field_length_limit integer DEFAULT 0 NOT NULL,
|
||||
elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL,
|
||||
elasticsearch_max_code_indexing_concurrency integer DEFAULT 30 NOT NULL,
|
||||
observability_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
|
||||
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
|
||||
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
|
||||
|
@ -7508,6 +7509,7 @@ CREATE TABLE application_settings (
|
|||
CONSTRAINT check_application_settings_elasticsearch_is_hash CHECK ((jsonb_typeof(elasticsearch) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_importers_is_hash CHECK ((jsonb_typeof(importers) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_integrations_is_hash CHECK ((jsonb_typeof(integrations) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_o11y_settings_is_hash CHECK ((jsonb_typeof(observability_settings) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_package_registry_is_hash CHECK ((jsonb_typeof(package_registry) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_rate_limits_is_hash CHECK ((jsonb_typeof(rate_limits) = 'object'::text)),
|
||||
CONSTRAINT check_application_settings_rate_limits_unauth_git_http_is_hash CHECK ((jsonb_typeof(rate_limits_unauthenticated_git_http) = 'object'::text)),
|
||||
|
|
|
@ -32488,10 +32488,6 @@ Returns [`ProjectDataTransfer`](#projectdatatransfer).
|
|||
|
||||
Software dependencies used by the project.
|
||||
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 15.9.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`DependencyConnection`](#dependencyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
|
@ -35198,6 +35194,7 @@ JSON structure of a file with matches.
|
|||
| <a id="searchblobfiletypeblameurl"></a>`blameUrl` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.2. **Status**: Experiment. Blame URL of the file. |
|
||||
| <a id="searchblobfiletypechunks"></a>`chunks` **{warning-solid}** | [`[SearchBlobChunk!]`](#searchblobchunk) | **Introduced** in GitLab 17.2. **Status**: Experiment. Maximum matches per file. |
|
||||
| <a id="searchblobfiletypefileurl"></a>`fileUrl` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.2. **Status**: Experiment. URL of the file. |
|
||||
| <a id="searchblobfiletypelanguage"></a>`language` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. Language of the file. |
|
||||
| <a id="searchblobfiletypematchcount"></a>`matchCount` **{warning-solid}** | [`Int`](#int) | **Introduced** in GitLab 17.2. **Status**: Experiment. Matches per file up to a max of 50 chunks. Default is 3. |
|
||||
| <a id="searchblobfiletypematchcounttotal"></a>`matchCountTotal` **{warning-solid}** | [`Int`](#int) | **Introduced** in GitLab 17.2. **Status**: Experiment. Total number of matches per file. |
|
||||
| <a id="searchblobfiletypepath"></a>`path` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.2. **Status**: Experiment. Path of the file. |
|
||||
|
@ -35211,6 +35208,7 @@ JSON structure of each line in a matched chunk.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="searchbloblinehighlights"></a>`highlights` **{warning-solid}** | [`[[Int!]!]`](#int) | **Introduced** in GitLab 17.8. **Status**: Experiment. Column numbers of the first and last highlighted characters on a line. |
|
||||
| <a id="searchbloblinelinenumber"></a>`lineNumber` **{warning-solid}** | [`Int`](#int) | **Introduced** in GitLab 17.2. **Status**: Experiment. Line number of the blob. |
|
||||
| <a id="searchbloblinerichtext"></a>`richText` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.2. **Status**: Experiment. Rich text of the blob. |
|
||||
| <a id="searchbloblinetext"></a>`text` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.2. **Status**: Experiment. Text content of the blob. |
|
||||
|
|
|
@ -582,6 +582,7 @@ To resolve these issues:
|
|||
- For new projects, set up and run the necessary security scans on the default branch before creating merge requests.
|
||||
- Consider using scan execution policies or pipeline execution policies to ensure consistent execution of security scans across all branches.
|
||||
- Consider using [`fallback_behavior`](#fallback_behavior) with `open` to prevent invalid or unenforceable rules in a policy from requiring approval.
|
||||
- Consider using the [`policy tuning`](#policy_tuning) setting `unblock_rules_using_execution_policies` to address scenarios where security scan artifacts are missing, and scan execution policies are enforced. When enabled, this setting makes approval rules optional when scan artifacts are missing from the target branch and a scan is required by a scan execution policy. This feature only works with an existing scan execution policy that has matching scanners. It offers flexibility in the merge request process when certain security scans cannot be performed due to missing artifacts.
|
||||
|
||||
### Support request for debugging of merge request approval policy
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ the only jobs that run are the pipeline execution policy jobs.
|
|||
|
||||
### `override_project_ci`
|
||||
|
||||
> - Updated handling of workflow rules [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175088) in GitLab 17.8 [with a flag](../../../administration/feature_flags.md) named `policies_always_override_project_ci`. Disabled by default.
|
||||
> - Updated handling of workflow rules [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175088) in GitLab 17.8 [with a flag](../../../administration/feature_flags.md) named `policies_always_override_project_ci`. Enabled by default.
|
||||
|
||||
This strategy replaces the project's existing CI/CD configuration with a new one defined by the pipeline execution policy. This strategy is ideal when the entire pipeline needs to be standardized or replaced, like when you want to enforce organization-wide CI/CD standards or compliance requirements in a highly regulated industry. To override the pipeline configuration, define the CI/CD jobs and do not use `include:project`.
|
||||
|
||||
|
|
|
@ -12,11 +12,10 @@ DETAILS:
|
|||
**Status:** Preview/Beta
|
||||
|
||||
> - Introduced as [beta](../../policy/development_stages_support.md#beta) in GitLab 17.7 [with a flag](../../administration/feature_flags.md) named `amazon_q_integration`. Disabled by default.
|
||||
> - Feature flag `amazon_q_integration` removed in GitLab 17.8.
|
||||
|
||||
FLAG:
|
||||
The availability of this feature is controlled by a feature flag.
|
||||
For more information, see the history.
|
||||
This feature is Preview/Beta and is available for testing, but not ready for production use.
|
||||
NOTE:
|
||||
If you have a Duo Pro or Duo Enterprise add-on, this feature is not available.
|
||||
|
||||
At Re:Invent 2024, Amazon announced the GitLab Duo with Amazon Q integration.
|
||||
With this integration, you can automate tasks and increase productivity.
|
||||
|
|
|
@ -12,11 +12,10 @@ DETAILS:
|
|||
**Status:** Preview/Beta
|
||||
|
||||
> - Introduced as an [experiment](../../policy/development_stages_support.md#experiment) in GitLab 17.7 [with a flag](../../administration/feature_flags.md) named `amazon_q_integration`. Disabled by default.
|
||||
> - Feature flag `amazon_q_integration` removed in GitLab 17.8.
|
||||
|
||||
FLAG:
|
||||
The availability of this feature is controlled by a feature flag.
|
||||
For more information, see the history.
|
||||
This feature is a Preview/Beta and is available for testing, but not ready for production use.
|
||||
NOTE:
|
||||
If you have a Duo Pro or Duo Enterprise add-on, this feature is not available.
|
||||
|
||||
To use GitLab Duo with Amazon Q, you can [request access to a lab environment](https://about.gitlab.com/partners/technology-partners/aws/#interest).
|
||||
|
||||
|
@ -40,7 +39,6 @@ To set up GitLab Duo with Amazon Q, you must:
|
|||
- With an HTTPS URL that can be accessed by Amazon Q (the SSL certificate must not be self-signed).
|
||||
For more details about SSL, see [Configure SSL for a Linux package installation](https://docs.gitlab.com/omnibus/settings/ssl/).
|
||||
- With an Ultimate subscription that is synchronized with GitLab. (No trial access.)
|
||||
- With the `amazon_q_integration` [feature flag enabled](../../administration/feature_flags.md).
|
||||
- GitLab Duo features [must be turned on](../gitlab_duo/turn_on_off.md#turn-on-beta-and-experimental-features).
|
||||
(Experimental and beta features are off by default.)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ GitLab plays the role of a MLflow server. Running `mlflow server` is not necessa
|
|||
|
||||
Prerequisites:
|
||||
|
||||
- A [personal](../../../../user/profile/personal_access_tokens.md), [project](../../../../user/project/settings/project_access_tokens.md), or [group](../../../../user/group/settings/group_access_tokens.md) access token with at least the Developer role and the `api` permission.
|
||||
- A [personal](../../../../user/profile/personal_access_tokens.md), [project](../../../../user/project/settings/project_access_tokens.md), or [group](../../../../user/group/settings/group_access_tokens.md) access token with at least the Developer role and the `api` scope.
|
||||
- The project ID. To find the project ID:
|
||||
1. On the left sidebar, select **Search or go to** and find your project.
|
||||
1. Select **Settings > General**.
|
||||
|
@ -46,19 +46,19 @@ by selecting the vertical ellipsis (**{ellipsis_v}**).
|
|||
## Model experiments
|
||||
|
||||
When running the training code, MLflow client can be used to create experiments, runs,
|
||||
models, model versions, log parameters, metrics, metadata and artifacts on GitLab.
|
||||
models, model versions, log parameters, metrics, metadata, and artifacts on GitLab.
|
||||
|
||||
After experiments are logged, they are listed under `/<your project>/-/ml/experiments`.
|
||||
|
||||
Runs are registered as candidates, which can be explored by selecting an experiment, model, or model version.
|
||||
Runs are registered and can be explored by selecting an experiment, model, or model version.
|
||||
|
||||
### Associating a candidate to a CI/CD job
|
||||
### Associating a run to a CI/CD job
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119454) in GitLab 16.1.
|
||||
> - [Changed](https://gitlab.com/groups/gitlab-org/-/epics/9423) to beta in GitLab 17.1.
|
||||
|
||||
If your training code is being run from a CI/CD job, GitLab can use that information to enhance
|
||||
candidate metadata. To associate a candidate to a CI/CD job:
|
||||
run metadata. To associate a run to a CI/CD job:
|
||||
|
||||
1. In the [Project CI variables](../../../../ci/variables/index.md), include the following variables:
|
||||
- `MLFLOW_TRACKING_URI`: `"<your gitlab endpoint>/api/v4/projects/<your project id>/ml/mlflow"`
|
||||
|
@ -70,7 +70,7 @@ candidate metadata. To associate a candidate to a CI/CD job:
|
|||
import os
|
||||
import mlflow
|
||||
|
||||
with mlflow.start_run(run_name=f"Candidate {index}"):
|
||||
with mlflow.start_run(run_name=f"Run {index}"):
|
||||
# Your training code
|
||||
|
||||
# Start of snippet to be included
|
||||
|
@ -137,7 +137,7 @@ client.delete_registered_model(model_name)
|
|||
### Logging candidates to a model
|
||||
|
||||
Every model has an associated experiment with the same name prefixed by `[model]`.
|
||||
To log a candidate/run to the model, use the experiment passing the correct name:
|
||||
To log a run to the model, use the experiment passing the correct name:
|
||||
|
||||
```python
|
||||
from mlflow import MlflowClient
|
||||
|
@ -177,7 +177,7 @@ client.create_model_version(model_name, version, description=description, tags=t
|
|||
|
||||
**Notes**
|
||||
|
||||
- Argument `run_id` is ignored. Every model version behaves as a Candidate/Run. Creating a mode version from a run is not yet supported.
|
||||
- Argument `run_id` is ignored. Every model version behaves as a run. Creating a mode version from a run is not yet supported.
|
||||
- Argument `source` is ignored. GitLab will create a package location for the model version files.
|
||||
- Argument `run_link` is ignored.
|
||||
- Argument `await_creation_for` is ignored.
|
||||
|
@ -240,7 +240,7 @@ model = mlflow.pyfunc.load_model(f"models:/{model_name}/latest")
|
|||
|
||||
#### Logging metrics and parameters to a model version
|
||||
|
||||
Every model version is also a candidate/run, allowing users to log parameters
|
||||
Every model version is also a run, allowing users to log parameters
|
||||
and metrics. The run ID can either be found at the Model version page in GitLab,
|
||||
or by using the MLflow client:
|
||||
|
||||
|
@ -285,7 +285,7 @@ Artifacts will then be available under `https/<your project>/-/ml/models/<model_
|
|||
|
||||
#### Linking a model version to a CI/CD job
|
||||
|
||||
Similar to candidates, it is also possible to link a model version to a CI/CD job:
|
||||
Similar to runs, it is also possible to link a model version to a CI/CD job:
|
||||
|
||||
```python
|
||||
import os
|
||||
|
@ -318,7 +318,7 @@ of the methods below are also supported with the same caveats.
|
|||
| `set_experiment` | Yes | 15.11 | |
|
||||
| `get_run` | Yes | 15.11 | |
|
||||
| `delete_run` | Yes | 17.5 | |
|
||||
| `start_run` | Yes | 15.11 | (16.3) If a name is not provided, the candidate receives a random nickname. |
|
||||
| `start_run` | Yes | 15.11 | (16.3) If a name is not provided, the run receives a random nickname. |
|
||||
| `search_runs` | Yes | 15.11 | (16.4) `experiment_ids` supports only a single experiment ID with order by column or metric. |
|
||||
| `log_artifact` | Yes with caveat | 15.11 | (15.11) `artifact_path` must be empty. Does not support directories. |
|
||||
| `log_artifacts` | Yes with caveat | 15.11 | (15.11) `artifact_path` must be empty. Does not support directories. |
|
||||
|
|
|
@ -65,7 +65,6 @@ module API
|
|||
|
||||
resource do
|
||||
before do
|
||||
check_read_only_feature_flag_enabled!
|
||||
authorize! :admin_secure_files, user_project
|
||||
end
|
||||
|
||||
|
@ -116,10 +115,6 @@ module API
|
|||
def check_api_enabled!
|
||||
forbidden! unless Gitlab.config.ci_secure_files.enabled
|
||||
end
|
||||
|
||||
def check_read_only_feature_flag_enabled!
|
||||
service_unavailable! if Feature.enabled?(:ci_secure_files_read_only, user_project, type: :ops)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -193,32 +193,7 @@ module API
|
|||
'slack-slash-commands' => ::Integrations::SlackSlashCommands.api_arguments,
|
||||
'packagist' => ::Integrations::Packagist.api_arguments,
|
||||
'phorge' => ::Integrations::Phorge.api_arguments,
|
||||
'pipelines-email' => [
|
||||
{
|
||||
required: true,
|
||||
name: :recipients,
|
||||
type: String,
|
||||
desc: 'Comma-separated list of recipient email addresses'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :notify_only_broken_pipelines,
|
||||
type: ::Grape::API::Boolean,
|
||||
desc: 'Notify only broken pipelines'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :notify_only_default_branch,
|
||||
type: ::Grape::API::Boolean,
|
||||
desc: 'Send notifications only for the default branch'
|
||||
},
|
||||
{
|
||||
required: false,
|
||||
name: :branches_to_be_notified,
|
||||
type: String,
|
||||
desc: 'Branches for which notifications are to be sent'
|
||||
}
|
||||
],
|
||||
'pipelines-email' => ::Integrations::PipelinesEmail.api_arguments,
|
||||
'pivotaltracker' => ::Integrations::Pivotaltracker.api_arguments,
|
||||
'pumble' => ::Integrations::Pumble.api_arguments,
|
||||
'pushover' => ::Integrations::Pushover.api_arguments,
|
||||
|
|
|
@ -14,6 +14,8 @@ module Gitlab
|
|||
notifications: Gitlab::Agent::Notifications::Rpc::Notifications::Stub
|
||||
}.freeze
|
||||
|
||||
AUTOFLOW_CI_VARIABLE_ENV_SCOPE = 'autoflow/internal-use'
|
||||
|
||||
ConfigurationError = Class.new(StandardError)
|
||||
|
||||
def initialize
|
||||
|
@ -72,6 +74,9 @@ module Gitlab
|
|||
# We only want to send events if AutoFlow is enabled and no-op otherwise
|
||||
return unless Feature.enabled?(:autoflow_enabled, project)
|
||||
|
||||
# retrieve all AutoFlow-relevant variables
|
||||
variables = project.variables.by_environment_scope(AUTOFLOW_CI_VARIABLE_ENV_SCOPE)
|
||||
|
||||
project_proto = Gitlab::Agent::Event::Project.new(
|
||||
id: project.id,
|
||||
full_path: project.full_path
|
||||
|
@ -89,7 +94,8 @@ module Gitlab
|
|||
},
|
||||
text_data: data.to_json
|
||||
),
|
||||
flow_project: project_proto
|
||||
flow_project: project_proto,
|
||||
variables: variables.to_h { |v| [v.key, v.value] }
|
||||
)
|
||||
|
||||
stub_for(:autoflow)
|
||||
|
|
|
@ -80,7 +80,7 @@ module Gitlab
|
|||
def get_repo_url(project_full_path)
|
||||
return ssh_url(project_full_path) if Gitlab::CurrentSettings.enabled_git_access_protocol == 'ssh'
|
||||
|
||||
build_http_url(project_full_path)
|
||||
"#{http_url(project_full_path)}.git"
|
||||
end
|
||||
|
||||
# create_go_get_html_response creates a HTML document for go get with the expected meta tags.
|
||||
|
@ -100,11 +100,7 @@ module Gitlab
|
|||
# get_root_path returns a root path based on the instance URL
|
||||
# that includes a relative part of URL if it was set
|
||||
def get_root_path(project_full_path)
|
||||
if feature_flag_enabled?
|
||||
http_url(project_full_path).gsub(%r{\Ahttps?://}, '')
|
||||
else
|
||||
Gitlab::Utils.append_path(Gitlab.config.gitlab.host, project_full_path)
|
||||
end
|
||||
http_url(project_full_path).gsub(%r{\Ahttps?://}, '')
|
||||
end
|
||||
|
||||
# http_url returns a direct link to the project
|
||||
|
@ -112,19 +108,6 @@ module Gitlab
|
|||
Gitlab::Utils.append_path(Gitlab.config.gitlab.url, project_full_path)
|
||||
end
|
||||
|
||||
# build_http_url (temporary) constructs a http url
|
||||
def build_http_url(project_full_path)
|
||||
if feature_flag_enabled?
|
||||
"#{http_url(project_full_path)}.git"
|
||||
else
|
||||
Gitlab::RepositoryUrlBuilder.build(project_full_path, protocol: :http)
|
||||
end
|
||||
end
|
||||
|
||||
def feature_flag_enabled?
|
||||
Feature.enabled?(:go_get_handle_relative_url, Feature.current_request)
|
||||
end
|
||||
|
||||
# project_for_path searches for a project based on the path_info
|
||||
def project_for_path(path_info)
|
||||
project_path_match = "#{path_info}/".match(PROJECT_PATH_REGEX)
|
||||
|
|
|
@ -109,7 +109,8 @@ module Gitlab
|
|||
params 'path/to/project [--with_notes]'
|
||||
types Issue
|
||||
condition do
|
||||
quick_action_target.persisted? &&
|
||||
quick_action_target.try(:supports_move_and_clone?) &&
|
||||
quick_action_target.persisted? &&
|
||||
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
end
|
||||
command :clone do |params = ''|
|
||||
|
@ -144,7 +145,8 @@ module Gitlab
|
|||
params 'path/to/project'
|
||||
types Issue
|
||||
condition do
|
||||
quick_action_target.persisted? &&
|
||||
quick_action_target.try(:supports_move_and_clone?) &&
|
||||
quick_action_target.persisted? &&
|
||||
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
end
|
||||
command :move do |target_project_path|
|
||||
|
|
|
@ -38,9 +38,11 @@ module Gitlab
|
|||
issuable: target_type)
|
||||
end
|
||||
|
||||
attr_reader :target_type
|
||||
|
||||
private
|
||||
|
||||
attr_reader :target_type, :container_type
|
||||
attr_reader :container_type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,5 +29,13 @@ module Search
|
|||
def failed?(*)
|
||||
error.present?
|
||||
end
|
||||
|
||||
def blobs_count
|
||||
0
|
||||
end
|
||||
|
||||
def file_count
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,9 +25,8 @@ namespace :tw do
|
|||
CodeOwnerRule.new('AI Framework', '@sselhorn'),
|
||||
# CodeOwnerRule.new('AI Model Validation', ''),
|
||||
# CodeOwnerRule.new('Analytics Instrumentation', ''),
|
||||
# CodeOwnerRule.new('Anti-Abuse', ''),
|
||||
CodeOwnerRule.new('Authentication', '@idurham'),
|
||||
CodeOwnerRule.new('Authorization', '@rlehmann1'),
|
||||
CodeOwnerRule.new('Authorization', '@idurham'),
|
||||
CodeOwnerRule.new('Cloud Connector', '@jglassman1'),
|
||||
CodeOwnerRule.new('Code Creation', '@jglassman1'),
|
||||
CodeOwnerRule.new('Code Review', '@aqualls'),
|
||||
|
@ -96,8 +95,8 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Analytics Instrumentation',
|
||||
'@gitlab-org/analytics-section/product-analytics/engineers/frontend ' \
|
||||
'@gitlab-org/analytics-section/analytics-instrumentation/engineers'),
|
||||
CodeOwnerRule.new('Authentication', '@gitlab-org/govern/authentication/approvers'),
|
||||
CodeOwnerRule.new('Authorization', '@gitlab-org/govern/authorization/approvers'),
|
||||
CodeOwnerRule.new('Authentication', '@gitlab-org/software-supply-chain-security/authentication/approvers'),
|
||||
CodeOwnerRule.new('Authorization', '@gitlab-org/software-supply-chain-security/authorization/approvers'),
|
||||
CodeOwnerRule.new('Compliance',
|
||||
'@gitlab-org/govern/security-policies-frontend @gitlab-org/govern/threat-insights-frontend-team ' \
|
||||
'@gitlab-org/govern/threat-insights-backend-team'),
|
||||
|
|
|
@ -13926,7 +13926,7 @@ msgstr ""
|
|||
msgid "Comma-separated list of branches to be automatically inspected. Leave blank to include all branches."
|
||||
msgstr ""
|
||||
|
||||
msgid "Comma-separated list of email addresses."
|
||||
msgid "Comma-separated list of recipient email addresses."
|
||||
msgstr ""
|
||||
|
||||
msgid "Command"
|
||||
|
@ -17857,18 +17857,30 @@ msgstr ""
|
|||
msgid "DastProfiles|/graphql"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A URL that is compared to the URL in the browser to determine if authentication has succeeded after the login form is submitted."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A comma-separated list of actions to be run after login but before login verification. Currently supports `click` actions."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A comma-separated list of selectors representing elements to click on prior to entering the DAST_AUTH_USERNAME and DAST_AUTH_PASSWORD into the login form."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A cookie name and value to be added to every request."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A selector describing an element whose presence is used to determine if authentication has succeeded after the login form is submitted."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A selector describing the element that is clicked on to submit the username form of a multi-page login process."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -17899,9 +17911,15 @@ msgstr ""
|
|||
msgid "DastProfiles|Additional variables"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Advertise scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|After-login actions"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Allowed hosts"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Are you sure you want to delete this profile?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17914,6 +17932,12 @@ msgstr ""
|
|||
msgid "DastProfiles|Authentication URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Authentication delegation servers"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Authentication type"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Before-login actions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17929,7 +17953,10 @@ msgstr ""
|
|||
msgid "DastProfiles|Clear input fields"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Comma-separated list of check identifiers to use for the scan. For identifiers, see %{linkStart}vulnerability checks.%{linkEnd}"
|
||||
msgid "DastProfiles|Comma-separated list of selectors that are ignored when scanning."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Cookie names"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not create the scanner profile. Please try again."
|
||||
|
@ -17968,9 +17995,18 @@ msgstr ""
|
|||
msgid "DastProfiles|DAST profile library"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|DOM ready timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|DOM stable timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Debug messages"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Define how long to wait for updates to the DOM before checking a page is stable. Defaults to `500ms`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Delete"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17992,12 +18028,21 @@ msgstr ""
|
|||
msgid "DastProfiles|Edit site profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Element search timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Enable Authentication"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Enable Basic Authentication"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Ensures that the provided paths are always scanned. Set to a comma-separated list of URL paths relative to `DAST_TARGET_URL`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Ensures that the provided paths are always scanned. Set to a file path containing a list of URL paths relative to `DAST_TARGET_URL`. The file must be plain text with one path per line."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Enter URLs in a comma-separated list."
|
||||
msgstr ""
|
||||
|
||||
|
@ -18013,7 +18058,10 @@ msgstr ""
|
|||
msgid "DastProfiles|Excluded URLs (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Excluded checks"
|
||||
msgid "DastProfiles|Excluded elements"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Excluded hosts"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Excluded paths"
|
||||
|
@ -18022,21 +18070,51 @@ msgstr ""
|
|||
msgid "DastProfiles|Excluded paths (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Extract element timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Field must not be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|First submit field"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Hide debug messages"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Hostnames included in this variable are accessed, not attacked, and not reported against."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Hostnames included in this variable are considered excluded and connections are forcibly dropped."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Hostnames included in this variable are considered in scope when crawled. By default the `DAST_TARGET_URL` hostname is included in the allowed hosts list. Headers set using `DAST_REQUEST_HEADERS` are added to every request made to these hostnames."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Ignored hosts"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Include debug messages in the DAST console output."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Loading element"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Manage %{profileType} profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Manage profiles"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Maximum action count"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Maximum action depth"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Maximum response size (MB)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Minimum = 0 (no timeout enabled), Maximum = 2880 minutes"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18076,9 +18154,30 @@ msgstr ""
|
|||
msgid "DastProfiles|Not Validated"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Number of workers that passive scan in parallel. Defaults to the number of available CPUs."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|PKCS12 certificate"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|PKCS12 password"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Page ready timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Page ready timeout (after action)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Page ready timeout (after navigation)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Passive"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Passive scan worker count"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Password"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18094,6 +18193,12 @@ msgstr ""
|
|||
msgid "DastProfiles|Profile name"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Ready element"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Request cookies"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Request headers"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18145,6 +18250,24 @@ msgstr ""
|
|||
msgid "DastProfiles|Select site profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Selector that when detected as visible on the page, indicates to the analyzer that the page has finished loading and the scan can continue. Cannot be used with `DAST_PAGE_IS_LOADING_ELEMENT`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Selector that, when no longer visible on the page, indicates to the analyzer that the page has finished loading and the scan can continue. Cannot be used with `DAST_PAGE_IS_READY_ELEMENT`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Set to `false` to disable caching. Default: `true`. **Note:** Disabling cache can cause OOM events or DAST job timeouts."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Set to `true` to add a `Via` header to every request sent, advertising that the request was sent as part of a GitLab DAST scan. Default: `false`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Set to `true` to prevent DAST from checking that the target is available before scanning. Default: `false`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Set to a comma-separated list of cookie names to specify which cookies are used for authentication."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Show debug messages"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18160,30 +18283,93 @@ msgstr ""
|
|||
msgid "DastProfiles|Site type"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Skip target check"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Submit button"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Submit button (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Success URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Success element"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Success without login form"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target check timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target paths"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target paths file"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Target timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The PKCS12 certificate used for sites that require Mutual TLS. Must be encoded as base64 text."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The authentication type to use."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to allow the browser to extract newly found elements or navigations. Defaults to `5s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to allow the browser to search for new elements or user actions. Defaults to `3s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after a navigation completes. Defaults to `6s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis. Defaults to `7s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for a browser to navigate from one page to another. Defaults to `15s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for an element before determining it is ready for analysis. Defaults to `300ms`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for the active scan phase of the scan to complete. Defaults to 3h."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum amount of time to wait for the crawl phase of the scan to complete. Defaults to `24h`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of actions that the crawler performs. Example actions include selecting a link, or filling out a form. Defaults to `10000`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of chained actions that the crawler takes. For example, `Click, Form Fill, Click` is a depth of three. Defaults to `10`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of concurrent browser instances to use. For instance runners on GitLab.com, we recommended a maximum of three. Private runners with more resources may benefit from a higher number, but are likely to produce little benefit after five to seven instances. The default value is dynamic, equal to the number of usable logical CPUs."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of minutes allowed for the crawler to traverse the site."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The maximum size of a HTTP response body. Responses with bodies larger than this are blocked by the browser. Defaults to `10` MB."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The number of active checks to run in parallel. Defaults to 3."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|The password of the certificate used in `DAST_PKCS12_CERTIFICATE_BASE64`. Create sensitive %{linkStart}custom CI/CI variables%{linkEnd} using the GitLab UI."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|This profile is currently being used in a policy."
|
||||
msgstr ""
|
||||
|
||||
|
@ -18193,6 +18379,12 @@ msgstr ""
|
|||
msgid "DastProfiles|This site profile is currently being used by a policy. To make edits you must remove it from the active policy."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Time limit in seconds to wait for target availability. Default: `60s`."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Timeout"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|URL"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18202,6 +18394,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Update variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Use cache"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Username"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18226,12 +18421,21 @@ msgstr ""
|
|||
msgid "DastProfiles|Variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Verifies successful authentication by checking for the absence of a login form after the login form has been submitted. This success check is enabled by default."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Website"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|What does each method do?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Which servers should be allowed for integrated authentication and delegation."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Worker count"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|You can either choose a passive scan or validate the target site from the site profile management page. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -20355,6 +20559,9 @@ msgstr ""
|
|||
msgid "Disable What's new"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disable fetching cloud alerts if this GitLab instance is isolated from the public internet, or does not use observe.gitlab.com for Observability features."
|
||||
msgstr ""
|
||||
|
||||
msgid "Disable for this project"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24034,6 +24241,9 @@ msgstr ""
|
|||
msgid "Fetch and check out this merge request's feature branch:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Fetch cloud observability alerts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Fetching incoming email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -52459,6 +52669,9 @@ msgstr ""
|
|||
msgid "Send notifications for broken pipelines."
|
||||
msgstr ""
|
||||
|
||||
msgid "Send notifications for the default branch."
|
||||
msgstr ""
|
||||
|
||||
msgid "Send report"
|
||||
msgstr ""
|
||||
|
||||
|
@ -61517,6 +61730,9 @@ msgstr ""
|
|||
msgid "UserMapping|Source name"
|
||||
msgstr ""
|
||||
|
||||
msgid "UserMapping|The file is being processed and you will receive an email when completed."
|
||||
msgstr ""
|
||||
|
||||
msgid "UserMapping|The invitation could not be accepted."
|
||||
msgstr ""
|
||||
|
||||
|
@ -61547,6 +61763,9 @@ msgstr ""
|
|||
msgid "UserMapping|You might have already accepted or rejected the reassignment, or the assignment might have been canceled."
|
||||
msgstr ""
|
||||
|
||||
msgid "UserMapping|You must upload a CSV file with a .csv file extension."
|
||||
msgstr ""
|
||||
|
||||
msgid "UserProfile|%{count} %{file}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -66407,7 +66626,7 @@ msgstr ""
|
|||
msgid "cannot assign a linked work item as a parent"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot assign a non-confidential work item to a confidential parent. Make the work item confidential and try again."
|
||||
msgid "cannot assign a non-confidential %{work_item_type} to a confidential parent. Make the %{work_item_type} confidential and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot be a date in the past"
|
||||
|
|
|
@ -118,7 +118,7 @@
|
|||
"apollo-upload-client": "15.0.0",
|
||||
"apollo3-cache-persist": "^0.14.1",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"autosize": "^5.0.1",
|
||||
"autosize": "^6.0.1",
|
||||
"axios": "^0.24.0",
|
||||
"babel-loader": "^8.4.1",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
|
@ -129,7 +129,7 @@
|
|||
"colord": "^2.9.3",
|
||||
"compression-webpack-plugin": "^5.0.2",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"core-js": "^3.39.0",
|
||||
"core-js": "^3.40.0",
|
||||
"cron-validator": "^1.1.1",
|
||||
"cronstrue": "^1.122.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
diff --git a/node_modules/autosize/dist/autosize.esm.js b/node_modules/autosize/dist/autosize.esm.js
|
||||
index 224b143..b95fb84 100644
|
||||
--- a/node_modules/autosize/dist/autosize.esm.js
|
||||
+++ b/node_modules/autosize/dist/autosize.esm.js
|
||||
@@ -1 +1 @@
|
||||
-var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){a({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",s),t.removeEventListener("input",i),window.removeEventListener("resize",s),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",s),t.addEventListener("input",i),window.addEventListener("resize",s),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:s}),s()}function a(e){var o,i,l=e.restoreTextAlign,s=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",s&&(t.style.textAlign=s),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),c!==n.overflow&&!s)){var v=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===v?"end":"start"),a({restoreTextAlign:v,testForHeightReduction:!0})}}function s(){a({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;export default n;
|
||||
+var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){s({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",a),t.removeEventListener("input",i),window.removeEventListener("resize",a),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",a),t.addEventListener("input",i),window.addEventListener("resize",a),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:a}),a()}function s(e){var o,i,l=e.restoreTextAlign,a=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.scrollTop!==o&&(t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null)})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",a&&(t.style.textAlign=a),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),c!==n.overflow&&!a)){var p=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===p?"end":"start"),s({restoreTextAlign:p,testForHeightReduction:!0})}}function a(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;export default n;
|
||||
diff --git a/node_modules/autosize/dist/autosize.js b/node_modules/autosize/dist/autosize.js
|
||||
index e227c1a..4be6b3e 100644
|
||||
--- a/node_modules/autosize/dist/autosize.js
|
||||
+++ b/node_modules/autosize/dist/autosize.js
|
||||
@@ -24,6 +24,7 @@
|
||||
return arr.forEach(function (_ref) {
|
||||
var node = _ref[0],
|
||||
scrollTop = _ref[1];
|
||||
+ if (node.scrollTop === scrollTop) return;
|
||||
node.style.scrollBehavior = 'auto';
|
||||
node.scrollTop = scrollTop;
|
||||
node.style.scrollBehavior = null;
|
||||
diff --git a/node_modules/autosize/dist/autosize.min.js b/node_modules/autosize/dist/autosize.min.js
|
||||
index 1b24155..ccb470d 100644
|
||||
--- a/node_modules/autosize/dist/autosize.min.js
|
||||
+++ b/node_modules/autosize/dist/autosize.min.js
|
||||
@@ -1 +1 @@
|
||||
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).autosize=t()}(this,function(){var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;return"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){s({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",a),t.removeEventListener("input",i),window.removeEventListener("resize",a),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",a),t.addEventListener("input",i),window.addEventListener("resize",a),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:a}),a()}function s(e){var o,i,l=e.restoreTextAlign,a=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,f=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",a&&(t.style.textAlign=a),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),f!==n.overflow&&!a)){var c=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===c?"end":"start"),s({restoreTextAlign:c,testForHeightReduction:!0})}}function a(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e}),r});
|
||||
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e||self).autosize=t()}(this,function(){var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;return"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){s({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",a),t.removeEventListener("input",i),window.removeEventListener("resize",a),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",a),t.addEventListener("input",i),window.addEventListener("resize",a),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:a}),a()}function s(e){var o,i,l=e.restoreTextAlign,a=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,f=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.scrollTop!==o&&(t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null)})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",a&&(t.style.textAlign=a),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),f!==n.overflow&&!a)){var c=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===c?"end":"start"),s({restoreTextAlign:c,testForHeightReduction:!0})}}function a(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e}),r});
|
||||
diff --git a/node_modules/autosize/src/autosize.js b/node_modules/autosize/src/autosize.js
|
||||
index c3f1a13..02a627a 100644
|
||||
--- a/node_modules/autosize/src/autosize.js
|
||||
+++ b/node_modules/autosize/src/autosize.js
|
||||
@@ -16,6 +16,7 @@ function assign(ta) {
|
||||
}
|
||||
|
||||
return ()=> arr.forEach(([node, scrollTop]) => {
|
||||
+ if (node.scrollTop === scrollTop) return;
|
||||
node.style.scrollBehavior = 'auto';
|
||||
node.scrollTop = scrollTop;
|
||||
node.style.scrollBehavior = null;
|
|
@ -45,7 +45,6 @@ ee/spec/frontend/geo_sites/components/details/secondary_site/geo_site_replicatio
|
|||
ee/spec/frontend/geo_sites/index_spec.js
|
||||
ee/spec/frontend/groups/components/invite_members_spec.js
|
||||
ee/spec/frontend/groups/settings/components/comma_separated_list_token_selector_spec.js
|
||||
ee/spec/frontend/insights/insights_router_spec.js
|
||||
ee/spec/frontend/issues_analytics/components/issues_analytics_table_spec.js
|
||||
ee/spec/frontend/iterations/components/iteration_form_spec.js
|
||||
ee/spec/frontend/iterations/components/iteration_report_issues_spec.js
|
||||
|
|
|
@ -7,7 +7,6 @@ RSpec.describe 'Secure Files', :js, feature_category: :secrets_management do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(ci_secure_files_read_only: false)
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
Source host,Import type,Source user identifier,Source user name,Source username,GitLab username,GitLab public email
|
||||
example.com,github,alice_1,Alice Alison,alice,alice-gl,alice@example.com
|
||||
example.com,github,bob_1,Bob Bobson,bob,,bob@example.com
|
||||
example.com,github,carl_1,Carl Calson,carl,carl-gl,
|
||||
example.com,github,denise_1,Denise Denison,denise,denise-gl,denise@example.com
|
||||
example.com,github,eric_1,Eric Ericson,eric,eric-gl,eric@example.com
|
||||
example.com,github,frank_1,Secretive Frank,frank,,frank-secret@example.com
|
|
|
@ -63,7 +63,7 @@ describe('diff_stats', () => {
|
|||
it("renders the bytes changes instead of line changes when the file isn't diffable", () => {
|
||||
const content = getBytesContainer();
|
||||
|
||||
expect(content.classes('gl-text-green-600')).toBe(true);
|
||||
expect(content.classes('gl-text-success')).toBe(true);
|
||||
expect(content.text()).toBe('+1.00 KiB (+100%)');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -218,7 +218,7 @@ describe('diff_file utilities', () => {
|
|||
{
|
||||
changed: 1024,
|
||||
percent: 100,
|
||||
classes: 'gl-text-green-600',
|
||||
classes: 'gl-text-success',
|
||||
sign: '+',
|
||||
text: '+1.00 KiB (+100%)',
|
||||
valid: true,
|
||||
|
@ -234,7 +234,7 @@ describe('diff_file utilities', () => {
|
|||
{
|
||||
changed: -1024,
|
||||
percent: -100,
|
||||
classes: 'gl-text-red-500',
|
||||
classes: 'gl-text-danger',
|
||||
sign: '',
|
||||
text: '-1.00 KiB (-100%)',
|
||||
valid: true,
|
||||
|
|
|
@ -3,7 +3,11 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { createAlert } from '~/alert';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import {
|
||||
HTTP_STATUS_OK,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import CsvUploadModal from '~/members/placeholders/components/csv_upload_modal.vue';
|
||||
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
|
||||
|
@ -106,14 +110,45 @@ describe('CsvUploadModal', () => {
|
|||
expect(axios.post).toHaveBeenCalledWith(MOCK_REASSIGNMENT_CSV_PATH, expectedFormData);
|
||||
});
|
||||
|
||||
describe('when the request fails', () => {
|
||||
beforeEach(() => {
|
||||
describe('when the request succeeds', () => {
|
||||
it('displays the message from the response', async () => {
|
||||
const mockMessage = 'file is being processed';
|
||||
|
||||
mockAxios
|
||||
.onPost(MOCK_REASSIGNMENT_CSV_PATH)
|
||||
.reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, { error: new Error('error uploading CSV') });
|
||||
.reply(HTTP_STATUS_OK, { message: mockMessage });
|
||||
|
||||
findGlModal().vm.$emit('primary');
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: mockMessage,
|
||||
variant: 'success',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the request fails', () => {
|
||||
it('displays the message from the response if present', async () => {
|
||||
const mockMessage = 'file too large';
|
||||
|
||||
mockAxios
|
||||
.onPost(MOCK_REASSIGNMENT_CSV_PATH)
|
||||
.reply(HTTP_STATUS_UNPROCESSABLE_ENTITY, { message: mockMessage });
|
||||
|
||||
findGlModal().vm.$emit('primary');
|
||||
await waitForPromises();
|
||||
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: mockMessage,
|
||||
});
|
||||
});
|
||||
|
||||
it('display an alert error message', async () => {
|
||||
mockAxios
|
||||
.onPost(MOCK_REASSIGNMENT_CSV_PATH)
|
||||
.reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, { error: new Error('error uploading CSV') });
|
||||
|
||||
findGlModal().vm.$emit('primary');
|
||||
await waitForPromises();
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ exports[`Merge request dashboard merge request component renders template 1`] =
|
|||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="gl-flex gl-font-bold gl-items-center gl-text-green-600"
|
||||
class="gl-flex gl-font-bold gl-items-center gl-text-success"
|
||||
>
|
||||
<span>
|
||||
+
|
||||
|
@ -94,7 +94,7 @@ exports[`Merge request dashboard merge request component renders template 1`] =
|
|||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="gl-flex gl-font-bold gl-items-center gl-text-red-500"
|
||||
class="gl-flex gl-font-bold gl-items-center gl-text-danger"
|
||||
>
|
||||
<span>
|
||||
−
|
||||
|
@ -167,6 +167,7 @@ exports[`Merge request dashboard merge request component when newListsEnabled is
|
|||
>
|
||||
<gl-icon-stub
|
||||
class="gl-mt-1"
|
||||
data-testid="mr-broken-badge"
|
||||
name="warning-solid"
|
||||
size="16"
|
||||
title="Cannot be merged automatically"
|
||||
|
|
|
@ -72,6 +72,7 @@ describe('Merge requests app component', () => {
|
|||
lists: lists || [
|
||||
[
|
||||
{
|
||||
id: 'assigned',
|
||||
title: 'Assigned merge requests',
|
||||
query: 'assignedMergeRequests',
|
||||
variables: { state: 'opened' },
|
||||
|
@ -120,6 +121,7 @@ describe('Merge requests app component', () => {
|
|||
[
|
||||
[
|
||||
{
|
||||
id: 'assigned',
|
||||
title: 'Assigned merge requests',
|
||||
query: 'assignedMergeRequests',
|
||||
variables: { state: 'opened' },
|
||||
|
@ -140,6 +142,7 @@ describe('Merge requests app component', () => {
|
|||
[
|
||||
[
|
||||
{
|
||||
id: 'assigned',
|
||||
title: 'Assigned merge requests',
|
||||
query: 'assignedMergeRequests',
|
||||
variables: { state: 'opened' },
|
||||
|
@ -147,6 +150,7 @@ describe('Merge requests app component', () => {
|
|||
],
|
||||
[
|
||||
{
|
||||
id: 'reviewer',
|
||||
title: 'Assigned merge requests',
|
||||
query: 'assignedMergeRequests',
|
||||
variables: { state: 'opened' },
|
||||
|
|
|
@ -11,6 +11,8 @@ Vue.use(VueApollo);
|
|||
describe('Merge request dashboard merge request component', () => {
|
||||
let wrapper;
|
||||
|
||||
const findBrokenBadge = () => wrapper.findByTestId('mr-broken-badge');
|
||||
|
||||
function createComponent(mergeRequest = {}, newListsEnabled = false) {
|
||||
const mockApollo = createMockApollo();
|
||||
|
||||
|
@ -22,6 +24,7 @@ describe('Merge request dashboard merge request component', () => {
|
|||
propsData: {
|
||||
listId: 'returned_to_you',
|
||||
mergeRequest: {
|
||||
state: 'opened',
|
||||
reference: '!123456',
|
||||
title: 'Merge request title',
|
||||
author: {
|
||||
|
@ -117,5 +120,16 @@ describe('Merge request dashboard merge request component', () => {
|
|||
|
||||
expect(wrapper.findComponent(StatusBadge).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it.each`
|
||||
state | exists | test
|
||||
${'opened'} | ${true} | ${'renders'}
|
||||
${'closed'} | ${false} | ${'does not render'}
|
||||
${'merged'} | ${false} | ${'does not render'}
|
||||
`('$test broken badge when state is $state', ({ state, exists }) => {
|
||||
createComponent({ state }, true);
|
||||
|
||||
expect(findBrokenBadge().exists()).toBe(exists);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ export function createMockMergeRequest(mergeRequest = {}) {
|
|||
title: 'Title',
|
||||
webUrl: '/',
|
||||
draft: false,
|
||||
state: 'opened',
|
||||
author: {
|
||||
id: 1,
|
||||
avatarUrl: '/',
|
||||
|
|
|
@ -40,7 +40,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
|
|||
id="reference-0"
|
||||
placeholder="Describe what your snippet does or how to use it…"
|
||||
rows="3"
|
||||
style="overflow-x: hidden; word-wrap: break-word; overflow-y: hidden;"
|
||||
style="overflow-x: hidden; word-wrap: break-word;"
|
||||
/>
|
||||
<div
|
||||
class="div-dropzone-hover"
|
||||
|
|
|
@ -10,6 +10,7 @@ import unSnoozeTodoMutation from '~/todos/components/mutations/un_snooze_todo.mu
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { useFakeDate } from 'helpers/fake_date';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import { mockTracking, unmockTracking } from 'jest/__helpers__/tracking_helper';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
|
@ -140,21 +141,26 @@ describe('ToggleSnoozedStatus', () => {
|
|||
});
|
||||
|
||||
it.each`
|
||||
index | expectedDate
|
||||
${0} | ${'2024-12-18T14:24:00.000Z'}
|
||||
${1} | ${'2024-12-18T17:24:00.000Z'}
|
||||
${2} | ${'2024-12-19T08:00:00.000Z'}
|
||||
index | expectedDate | expectedTrackingLabel
|
||||
${0} | ${'2024-12-18T14:24:00.000Z'} | ${'snooze_for_one_hour'}
|
||||
${1} | ${'2024-12-18T17:24:00.000Z'} | ${'snooze_until_later_today'}
|
||||
${2} | ${'2024-12-19T08:00:00.000Z'} | ${'snooze_until_tomorrow'}
|
||||
`(
|
||||
'triggers the snooze action with snoozeUntil = $expectedDate when clicking option #$index',
|
||||
({ index, expectedDate }) => {
|
||||
({ index, expectedDate, expectedTrackingLabel }) => {
|
||||
createComponent({ props: { isSnoozed: false, isPending: true } });
|
||||
|
||||
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
getPredefinedSnoozingOption(index).action();
|
||||
|
||||
expect(snoozeTodoMutationSuccessHandler).toHaveBeenCalledWith({
|
||||
snoozeUntil: new Date(expectedDate),
|
||||
todoId: mockTodo.id,
|
||||
});
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_todo_item_action', {
|
||||
label: expectedTrackingLabel,
|
||||
});
|
||||
|
||||
unmockTracking();
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -265,13 +271,18 @@ describe('ToggleSnoozedStatus', () => {
|
|||
props: { isSnoozed: true, isPending: true },
|
||||
unSnoozeTodoMutationHandler: jest.fn().mockRejectedValue(),
|
||||
});
|
||||
|
||||
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
||||
findUnSnoozeButton().vm.$emit('click');
|
||||
await waitForPromises();
|
||||
|
||||
expect(mockToastShow).toHaveBeenCalledWith('Failed to un-snooze todo. Try again later.', {
|
||||
variant: 'danger',
|
||||
});
|
||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_todo_item_action', {
|
||||
label: 'remove_snooze',
|
||||
});
|
||||
|
||||
unmockTracking();
|
||||
});
|
||||
|
||||
it('has a tooltip attached', () => {
|
||||
|
|
|
@ -79,7 +79,7 @@ describe('Diff Stats Dropdown', () => {
|
|||
expect(fileText).toContain(mockFiles[1].name);
|
||||
expect(fileText).toContain(mockFiles[1].path);
|
||||
expect(fileData.findComponent(GlIcon).props('name')).toEqual(mockFiles[1].icon);
|
||||
expect(fileData.findComponent(GlIcon).classes()).toContain('gl-text-red-500');
|
||||
expect(fileData.findComponent(GlIcon).classes()).toContain('gl-text-danger');
|
||||
expect(fileData.find('a').attributes('href')).toEqual(mockFiles[1].href);
|
||||
});
|
||||
|
||||
|
|
|
@ -260,13 +260,6 @@ describe('vue_shared/component/markdown/markdown_editor', () => {
|
|||
});
|
||||
|
||||
describe('autosize', () => {
|
||||
it('autosizes the textarea when the value changes', async () => {
|
||||
buildWrapper();
|
||||
await findTextarea().setValue('Lots of newlines\n\n\n\n\n\n\nMore content\n\n\nand newlines');
|
||||
await nextTick();
|
||||
expect(Autosize.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('autosizes the textarea when the value changes from outside the component', async () => {
|
||||
buildWrapper();
|
||||
wrapper.setProps({ value: 'Lots of newlines\n\n\n\n\n\n\nMore content\n\n\nand newlines' });
|
||||
|
|
|
@ -342,7 +342,6 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
|
|||
new_merge_request_path: project_new_merge_request_path(project),
|
||||
show_export_button: 'true',
|
||||
issuable_type: :merge_request,
|
||||
issuable_count: 5,
|
||||
email: current_user.notification_email_or_default,
|
||||
export_csv_path: '/csv-url',
|
||||
rss_url: '/rss-url',
|
||||
|
@ -455,7 +454,6 @@ RSpec.describe MergeRequestsHelper, feature_category: :code_review_workflow do
|
|||
is_public_visibility_restricted: 'false',
|
||||
is_signed_in: 'true',
|
||||
issuable_type: :merge_request,
|
||||
issuable_count: 5,
|
||||
email: current_user.notification_email_or_default,
|
||||
rss_url: "/rss-url",
|
||||
releases_endpoint: group_releases_path(group, format: :json),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Kas::Client do
|
||||
RSpec.describe Gitlab::Kas::Client, feature_category: :deployment_management do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:agent) { create(:cluster_agent, project: project) }
|
||||
|
||||
|
@ -146,6 +146,9 @@ RSpec.describe Gitlab::Kas::Client do
|
|||
end
|
||||
|
||||
context 'when autoflow_enabled FF is enabled' do
|
||||
let_it_be(:autoflow_var1) { create(:ci_variable, project: project, key: 'test_key_1', value: 'test-value-1', environment_scope: 'autoflow/internal-use') }
|
||||
let_it_be(:autoflow_var2) { create(:ci_variable, project: project, key: 'test_key_2', value: 'test-value-2', environment_scope: 'autoflow/internal-use') }
|
||||
let_it_be(:other_var) { create(:ci_variable, project: project, key: 'test_key_3', value: 'test-value-3') }
|
||||
let(:stub) { instance_double(Gitlab::Agent::AutoFlow::Rpc::AutoFlow::Stub) }
|
||||
let(:request) { instance_double(Gitlab::Agent::AutoFlow::Rpc::CloudEventRequest) }
|
||||
let(:event_param) { instance_double(Gitlab::Agent::Event::CloudEvent) }
|
||||
|
@ -175,7 +178,14 @@ RSpec.describe Gitlab::Kas::Client do
|
|||
.and_return(event_param)
|
||||
|
||||
expect(Gitlab::Agent::AutoFlow::Rpc::CloudEventRequest).to receive(:new)
|
||||
.with(event: event_param, flow_project: project_param)
|
||||
.with(
|
||||
event: event_param,
|
||||
flow_project: project_param,
|
||||
variables: {
|
||||
"test_key_1" => "test-value-1",
|
||||
"test_key_2" => "test-value-2"
|
||||
}
|
||||
)
|
||||
.and_return(request)
|
||||
|
||||
expect(stub).to receive(:cloud_event)
|
||||
|
|
|
@ -64,16 +64,6 @@ RSpec.describe Gitlab::Middleware::Go, feature_category: :source_code_management
|
|||
it 'returns the full project path' do
|
||||
expect_response_with_path(go, enabled_protocol, project.full_path, url_based: true)
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(go_get_handle_relative_url: false)
|
||||
end
|
||||
|
||||
it 'returns the full project path' do
|
||||
expect_response_with_path(go, enabled_protocol, project.full_path, url_based: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project is private' do
|
||||
|
|
|
@ -17,6 +17,18 @@ RSpec.describe ::Search::EmptySearchResults, feature_category: :global_search do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#blobs_count' do
|
||||
it 'returns a zero' do
|
||||
expect(results.blobs_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#file_count' do
|
||||
it 'returns a zero' do
|
||||
expect(results.file_count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#highlight_map' do
|
||||
it 'returns an empty hash' do
|
||||
expect(results.highlight_map).to eq({})
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Integrations::PipelinesEmail, :mailer do
|
||||
RSpec.describe Integrations::PipelinesEmail, :mailer, feature_category: :integrations do
|
||||
let(:pipeline) do
|
||||
create(:ci_pipeline, :failed,
|
||||
project: project,
|
||||
|
|
|
@ -263,7 +263,7 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
|
||||
it 'returns quick action commands supported for all work items' do
|
||||
is_expected.to include(:title, :reopen, :close, :cc, :tableflip, :shrug, :type, :promote_to, :checkin_reminder,
|
||||
:subscribe, :unsubscribe, :confidential, :award)
|
||||
:subscribe, :unsubscribe, :confidential, :award, :move, :clone)
|
||||
end
|
||||
|
||||
it 'omits quick action commands from assignees widget' do
|
||||
|
|
|
@ -250,6 +250,23 @@ RSpec.describe WorkItems::ParentLink, feature_category: :portfolio_management do
|
|||
expect(link.valid?).to eq(valid)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent is confidential' do
|
||||
before do
|
||||
issue.confidential = true
|
||||
task1.confidential = false
|
||||
end
|
||||
|
||||
it 'sets the correct error message' do
|
||||
link = build(:parent_link, work_item_parent: issue, work_item: task1)
|
||||
|
||||
link.valid?
|
||||
|
||||
expect(link.errors[:work_item]).to include(
|
||||
'cannot assign a non-confidential task to a confidential parent. ' \
|
||||
'Make the task confidential and try again.')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,6 @@ require 'spec_helper'
|
|||
RSpec.describe API::Ci::SecureFiles, feature_category: :mobile_devops do
|
||||
before do
|
||||
stub_ci_secure_file_object_storage
|
||||
stub_feature_flags(ci_secure_files_read_only: false)
|
||||
end
|
||||
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
|
@ -24,63 +23,6 @@ RSpec.describe API::Ci::SecureFiles, feature_category: :mobile_devops do
|
|||
end
|
||||
|
||||
describe 'GET /projects/:id/secure_files' do
|
||||
context 'ci_secure_files_read_only feature flag' do
|
||||
context 'when the flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_secure_files_read_only: true)
|
||||
end
|
||||
|
||||
it 'returns a 503 when attempting to upload a file' do
|
||||
stub_feature_flags(ci_secure_files_read_only: true)
|
||||
|
||||
expect do
|
||||
post api("/projects/#{project.id}/secure_files", maintainer), params: file_params
|
||||
end.not_to change { project.secure_files.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
end
|
||||
|
||||
it 'returns a 200 when downloading a file' do
|
||||
stub_feature_flags(ci_secure_files_read_only: true)
|
||||
|
||||
get api("/projects/#{project.id}/secure_files", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to be_a(Array)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature is disabled at the instance level' do
|
||||
before do
|
||||
stub_config(ci_secure_files: { enabled: false })
|
||||
end
|
||||
|
||||
it 'returns a 403 when attempting to upload a file' do
|
||||
expect do
|
||||
post api("/projects/#{project.id}/secure_files", maintainer), params: file_params
|
||||
end.not_to change { project.secure_files.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'returns a 403 when downloading a file' do
|
||||
get api("/projects/#{project.id}/secure_files", developer)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the flag is disabled' do
|
||||
it 'returns a 201 when uploading a file when the ci_secure_files_read_only feature flag is disabled' do
|
||||
expect do
|
||||
post api("/projects/#{project.id}/secure_files", maintainer), params: file_params
|
||||
end.to change { project.secure_files.count }.by(1)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'authenticated user with admin permissions' do
|
||||
it 'returns project secure files' do
|
||||
get api("/projects/#{project.id}/secure_files", maintainer)
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Groups::BulkPlaceholderAssignmentsController, feature_category: :importers do
|
||||
include WorkhorseHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group, :public, owners: user) }
|
||||
let(:file) { fixture_file_upload('spec/fixtures/import/user_mapping/user_mapping_upload.csv') }
|
||||
|
||||
describe 'GET /groups/*group_id/-/group_members/bulk_reassignment_file' do
|
||||
subject(:request) do
|
||||
get group_bulk_reassignment_file_path(group_id: group)
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'responds with CSV data' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
end
|
||||
|
||||
context 'and the user is not a group owner' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the CSV is not generated properly' do
|
||||
before do
|
||||
allow_next_instance_of(Import::SourceUsers::GenerateCsvService) do |service|
|
||||
allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'my error message'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects with an error' do
|
||||
request
|
||||
|
||||
expect(response).to be_redirect
|
||||
expect(flash[:alert]).to eq('my error message')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :importer_user_mapping_reassignment_csv is disabled' do
|
||||
before do
|
||||
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
|
||||
end
|
||||
|
||||
it 'responds with 404' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /groups/*group_id/-/group_members/bulk_reassignment_file' do
|
||||
let(:file) { fixture_file_upload('spec/fixtures/import/user_mapping/user_mapping_upload.csv') }
|
||||
|
||||
subject(:request) do
|
||||
workhorse_post_with_file(
|
||||
group_bulk_reassignment_file_path(group_id: group, format: :json),
|
||||
file_key: :file,
|
||||
params: { file: file }
|
||||
)
|
||||
end
|
||||
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'responds with success' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'and the user is not a group owner' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the file is not a CSV' do
|
||||
let(:file) { fixture_file_upload('spec/fixtures/dk.png') }
|
||||
|
||||
it 'returns unprocessable_entity' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :importer_user_mapping_reassignment_csv is disabled' do
|
||||
before do
|
||||
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
|
||||
end
|
||||
|
||||
it 'responds with 404' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /groups/*group_id/-/group_members/bulk_reassignment_file/authorize' do
|
||||
include_context 'workhorse headers'
|
||||
|
||||
subject(:request) do
|
||||
post authorize_group_bulk_reassignment_file_path(group_id: group, format: :json),
|
||||
params: { file: file },
|
||||
headers: workhorse_headers
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'responds with success' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,73 +30,4 @@ RSpec.describe Groups::GroupMembersController, feature_category: :groups_and_pro
|
|||
|
||||
it_behaves_like 'request_accessable'
|
||||
end
|
||||
|
||||
describe 'GET /groups/*group_id/-/group_members/bulk_reassignment_file' do
|
||||
let_it_be(:membershipable) do
|
||||
create(:group, :public).tap do |group|
|
||||
group.add_owner(user)
|
||||
end
|
||||
end
|
||||
|
||||
subject(:request) do
|
||||
get bulk_reassignment_file_group_group_members_path(group_id: membershipable)
|
||||
end
|
||||
|
||||
context 'when not signed in' do
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when signed in' do
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'responds with CSV data' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
end
|
||||
|
||||
context 'and the user is not a group owner' do
|
||||
let_it_be(:membershipable) { create(:group, :public) }
|
||||
|
||||
it 'forbids access to the endpoint' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the CSV is not generated properly' do
|
||||
before do
|
||||
allow_next_instance_of(Import::SourceUsers::GenerateCsvService) do |service|
|
||||
allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'my error message'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects with an error' do
|
||||
request
|
||||
|
||||
expect(response).to be_redirect
|
||||
expect(flash[:alert]).to eq('my error message')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :importer_user_mapping_reassignment_csv is disabled' do
|
||||
before do
|
||||
stub_feature_flags(importer_user_mapping_reassignment_csv: false)
|
||||
end
|
||||
|
||||
it 'responds with 404' do
|
||||
request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -51,6 +51,27 @@ RSpec.describe Issues::CloneService, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
# We will use this service in order to clone WorkItem to a new project. As WorkItem inherits from Issue, there
|
||||
# should not be any problem with passing a WorkItem instead of an Issue to this service.
|
||||
# Adding a small test case to cover this.
|
||||
context "when we pass a work_item" do
|
||||
include_context 'user can clone issue'
|
||||
|
||||
subject(:clone) { clone_service.execute(original_work_item, new_project) }
|
||||
|
||||
context "work item is of issue type" do
|
||||
let_it_be_with_reload(:original_work_item) { create(:work_item, :issue, project: old_project, author: author) }
|
||||
|
||||
it { expect { clone }.to change { new_project.issues.count }.by(1) }
|
||||
end
|
||||
|
||||
context "work item is of task type" do
|
||||
let_it_be_with_reload(:original_work_item) { create(:work_item, :task, project: old_project, author: author) }
|
||||
|
||||
it { expect { clone }.to raise_error(described_class::CloneError) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'generic issue' do
|
||||
let!(:new_issue) { clone_service.execute(old_issue, new_project, with_notes: with_notes) }
|
||||
|
||||
|
|
|
@ -44,6 +44,27 @@ RSpec.describe Issues::MoveService, feature_category: :team_planning do
|
|||
let!(:new_issue) { move_service.execute(old_issue, new_project) }
|
||||
end
|
||||
|
||||
# We will use this service in order to move WorkItem to a new project. As WorkItem inherits from Issue, there
|
||||
# should not be any problem with passing a WorkItem instead of an Issue to this service.
|
||||
# Adding a small test case to cover this.
|
||||
context "when we pass a work_item" do
|
||||
include_context 'user can move issue'
|
||||
|
||||
subject(:move) { move_service.execute(original_work_item, new_project) }
|
||||
|
||||
context "work item is of issue type" do
|
||||
let_it_be_with_reload(:original_work_item) { create(:work_item, :issue, project: old_project, author: author) }
|
||||
|
||||
it { expect { move }.to change { new_project.issues.count }.by(1) }
|
||||
end
|
||||
|
||||
context "work item is of task type" do
|
||||
let_it_be_with_reload(:original_work_item) { create(:work_item, :task, project: old_project, author: author) }
|
||||
|
||||
it { expect { move }.to raise_error(described_class::MoveError) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue creation fails' do
|
||||
include_context 'user can move issue'
|
||||
|
||||
|
|
|
@ -654,6 +654,43 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
|
||||
expect(message).to eq(_("Failed to move this issue because target project doesn't exist."))
|
||||
end
|
||||
|
||||
context "when we pass a work_item" do
|
||||
let(:work_item) { create(:work_item, :issue) }
|
||||
let(:move_command) { "/move #{project.full_path}" }
|
||||
|
||||
it '/move execution method message' do
|
||||
_, _, message = service.execute(move_command, work_item)
|
||||
|
||||
expect(message).to eq("Moved this issue to #{project.full_path}.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'clone issue command' do
|
||||
it 'returns the clone issue message' do
|
||||
_, _, message = service.execute("/clone #{project.full_path}", issue)
|
||||
translated_string = _("Cloned this issue to %{project_full_path}.")
|
||||
formatted_message = format(translated_string, project_full_path: project.full_path.to_s)
|
||||
|
||||
expect(message).to eq(formatted_message)
|
||||
end
|
||||
|
||||
it 'returns clone issue failure message when the referenced issue is not found' do
|
||||
_, _, message = service.execute('/clone invalid', issue)
|
||||
|
||||
expect(message).to eq(_("Failed to clone this issue because target project doesn't exist."))
|
||||
end
|
||||
|
||||
context "when we pass a work_item" do
|
||||
let(:work_item) { create(:work_item, :issue, project: project) }
|
||||
|
||||
it '/clone execution method message' do
|
||||
_, _, message = service.execute("/clone #{project.full_path}", work_item)
|
||||
|
||||
expect(message).to eq("Cloned this issue to #{project.full_path}.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'confidential command' do
|
||||
|
@ -3533,6 +3570,37 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
|
||||
expect(explanations).to eq([_("Moves this issue to test/project.")])
|
||||
end
|
||||
|
||||
context "when work item type is an issue" do
|
||||
let(:move_command) { "/move test/project" }
|
||||
let(:work_item) { create(:work_item, :issue, project: project) }
|
||||
|
||||
it "/move is available" do
|
||||
_, explanations = service.explain(move_command, work_item)
|
||||
|
||||
expect(explanations).to match_array(["Moves this issue to test/project."])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'clone issue to another project command' do
|
||||
let(:content) { '/clone test/project' }
|
||||
|
||||
it 'includes the project name' do
|
||||
_, explanations = service.explain(content, issue)
|
||||
|
||||
expect(explanations).to match_array([_("Clones this issue, without comments, to test/project.")])
|
||||
end
|
||||
|
||||
context "when work item type is an issue" do
|
||||
let(:work_item) { create(:work_item, :issue, project: project) }
|
||||
|
||||
it "/clone is available" do
|
||||
_, explanations = service.explain("/clone test/project", work_item)
|
||||
|
||||
expect(explanations).to match_array(["Clones this issue, without comments, to test/project."])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'tag a commit' do
|
||||
|
@ -3952,5 +4020,25 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when target is a work item type of issue' do
|
||||
let(:target) { create(:work_item, :issue) }
|
||||
|
||||
context "when work_item supports move and clone commands" do
|
||||
it 'does recognize the actions' do
|
||||
expect(service.available_commands(target).pluck(:name)).to include(:move, :clone)
|
||||
end
|
||||
end
|
||||
|
||||
context "when work_item does not support move and clone commands" do
|
||||
before do
|
||||
allow(target).to receive(:supports_move_and_clone?).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not recognize the action' do
|
||||
expect(service.available_commands(target).pluck(:name)).not_to include(:move, :clone)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,9 +88,9 @@ internal/upload/destination/objectstore/upload_strategy.go:29: internal/upload/d
|
|||
internal/upload/destination/objectstore/uploader.go:5:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
|
||||
internal/upload/destination/objectstore/uploader.go:95:12: G401: Use of weak cryptographic primitive (gosec)
|
||||
internal/upload/exif/exif.go:103:10: G204: Subprocess launched with variable (gosec)
|
||||
internal/upstream/routes.go:170:74: `(*upstream).wsRoute` - `matchers` always receives `nil` (unparam)
|
||||
internal/upstream/routes.go:230: Function 'configureRoutes' is too long (339 > 60) (funlen)
|
||||
internal/upstream/routes.go:485: internal/upstream/routes.go:485: Line contains TODO/BUG/FIXME/NOTE/OPTIMIZE/HACK: "TODO: We should probably not return a HT..." (godox)
|
||||
internal/upstream/routes.go:171:74: `(*upstream).wsRoute` - `matchers` always receives `nil` (unparam)
|
||||
internal/upstream/routes.go:231: Function 'configureRoutes' is too long (340 > 60) (funlen)
|
||||
internal/upstream/routes.go:487: internal/upstream/routes.go:487: Line contains TODO/BUG/FIXME/NOTE/OPTIMIZE/HACK: "TODO: We should probably not return a HT..." (godox)
|
||||
internal/upstream/upstream.go:116: internal/upstream/upstream.go:116: Line contains TODO/BUG/FIXME/NOTE/OPTIMIZE/HACK: "TODO: move to LabKit https://gitlab.com/..." (godox)
|
||||
internal/zipartifacts/metadata.go:118:54: G115: integer overflow conversion int -> uint32 (gosec)
|
||||
internal/zipartifacts/open_archive.go:78:28: response body must be closed (bodyclose)
|
||||
|
|
|
@ -189,6 +189,7 @@ func TestAcceleratedUpload(t *testing.T) {
|
|||
{"POST", "/api/v4/projects/2412/packages/helm/api/stable/charts", true},
|
||||
{"POST", "/api/v4/projects/group%2Fproject/packages/helm/api/stable/charts", true},
|
||||
{"POST", "/api/v4/projects/group%2Fsubgroup%2Fproject/packages/helm/api/stable/charts", true},
|
||||
{"POST", "/groups/my-group/-/group_members/bulk_reassignment_file", true},
|
||||
}
|
||||
|
||||
allowedHashFunctions := map[string][]string{
|
||||
|
|
|
@ -60,6 +60,7 @@ const (
|
|||
gitProjectPattern = `^/.+\.git/`
|
||||
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
|
||||
projectPattern = `^/([^/]+/){1,}[^/]+/`
|
||||
groupPattern = `^/groups/([^/]+/){0,}[^/]+/`
|
||||
apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
|
||||
apiGroupPattern = apiPattern + `v4/groups/[^/]+`
|
||||
apiTopicPattern = apiPattern + `v4/topics`
|
||||
|
@ -390,7 +391,8 @@ func configureRoutes(u *upstream) {
|
|||
newRoute(apiPattern+`v4/projects/import`, "api_projects_import", railsBackend), mimeMultipartUploader),
|
||||
u.route("POST",
|
||||
newRoute(apiPattern+`v4/projects/import-relation`, "api_projects_import_relation", railsBackend), mimeMultipartUploader),
|
||||
|
||||
u.route("POST",
|
||||
newRoute(groupPattern+`-/group_members/bulk_reassignment_file`, "group_placeholder_assignment", railsBackend), mimeMultipartUploader),
|
||||
// Project Import via UI upload acceleration
|
||||
u.route("POST",
|
||||
newRoute(importPattern+`gitlab_project`, "import_gitlab_project", railsBackend), mimeMultipartUploader),
|
||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -1408,8 +1408,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/fonts/-/fonts-1.3.0.tgz#df89c1bb6714e4a8a5d3272568aa4de7fb337267"
|
||||
integrity sha512-DoMUIN3DqjEn7wvcxBg/b7Ite5fTdF5EmuOZoBRo2j0UBGweDXmNBi+9HrTZs4cBU660dOxcf1hATFcG3npbPg==
|
||||
|
||||
"@gitlab/noop@^1.0.0", jackspeak@^2.3.5, "jackspeak@npm:@gitlab/noop@1.0.0":
|
||||
name jackspeak
|
||||
"@gitlab/noop@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/noop/-/noop-1.0.0.tgz#b1ecb8ae6b2abf9b2e28927e4fbb05b7a1b2704b"
|
||||
integrity sha512-nOltttik5o2BjBo8LnyeTFzHoLpMY/XcCVOC+lm9ZwU+ivEam8wafacMF0KTbRn1KVrIoHYdo70QnqS+vJiOVw==
|
||||
|
@ -4530,10 +4529,10 @@ autoprefixer@^10.4.8:
|
|||
picocolors "^1.0.0"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
autosize@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/autosize/-/autosize-5.0.1.tgz#ed269b0fa9b7eb47627048a1bb3299e99e003a0f"
|
||||
integrity sha512-UIWUlE4TOVPNNj2jjrU39wI4hEYbneUypEqcyRmRFIx5CC2gNdg3rQr+Zh7/3h6egbBvm33TDQjNQKtj9Tk1HA==
|
||||
autosize@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/autosize/-/autosize-6.0.1.tgz#64ee78dd7029be959eddd3afbbd33235b957e10f"
|
||||
integrity sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==
|
||||
|
||||
available-typed-arrays@^1.0.7:
|
||||
version "1.0.7"
|
||||
|
@ -5562,10 +5561,10 @@ core-js-pure@^3.30.2:
|
|||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.0.tgz#4660033304a050215ae82e476bd2513a419fbb34"
|
||||
integrity sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew==
|
||||
|
||||
core-js@^3.29.1, core-js@^3.39.0, core-js@^3.6.5:
|
||||
version "3.39.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.39.0.tgz#57f7647f4d2d030c32a72ea23a0555b2eaa30f83"
|
||||
integrity sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==
|
||||
core-js@^3.29.1, core-js@^3.40.0, core-js@^3.6.5:
|
||||
version "3.40.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.40.0.tgz#2773f6b06877d8eda102fc42f828176437062476"
|
||||
integrity sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
|
@ -9278,6 +9277,11 @@ iterall@^1.2.1:
|
|||
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea"
|
||||
integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==
|
||||
|
||||
jackspeak@^2.3.5, "jackspeak@npm:@gitlab/noop@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/noop/-/noop-1.0.0.tgz#b1ecb8ae6b2abf9b2e28927e4fbb05b7a1b2704b"
|
||||
integrity sha512-nOltttik5o2BjBo8LnyeTFzHoLpMY/XcCVOC+lm9ZwU+ivEam8wafacMF0KTbRn1KVrIoHYdo70QnqS+vJiOVw==
|
||||
|
||||
jed@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4"
|
||||
|
|
Loading…
Reference in New Issue