Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
cdaeb986b5
commit
99b0fc34b2
|
|
@ -278,13 +278,6 @@ export default {
|
|||
'app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue',
|
||||
'app/assets/javascripts/vue_merge_request_widget/widgets/accessibility/index.vue',
|
||||
'app/assets/javascripts/vue_merge_request_widget/widgets/code_quality/index.vue',
|
||||
'app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue',
|
||||
'app/assets/javascripts/vue_shared/components/mr_more_dropdown.vue',
|
||||
'app/assets/javascripts/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue',
|
||||
'app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue',
|
||||
'app/assets/javascripts/vue_shared/components/registry/list_item.vue',
|
||||
'app/assets/javascripts/vue_shared/components/smart_virtual_list.vue',
|
||||
'app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue',
|
||||
'app/assets/javascripts/webhooks/components/form_custom_header_item.vue',
|
||||
'app/assets/javascripts/work_items/components/create_work_item.vue',
|
||||
'app/assets/javascripts/work_items/components/design_management/design_notes/design_discussion.vue',
|
||||
|
|
@ -489,7 +482,6 @@ export default {
|
|||
'ee/app/assets/javascripts/usage_quotas/code_suggestions/components/search_and_sort_bar.vue',
|
||||
'ee/app/assets/javascripts/usage_quotas/seats/components/statistics_seats_card.vue',
|
||||
'ee/app/assets/javascripts/usage_quotas/transfer/components/usage_by_month.vue',
|
||||
'ee/app/assets/javascripts/users/identity_verification/components/credit_card_verification.vue',
|
||||
'ee/app/assets/javascripts/vue_merge_request_widget/components/blocking_merge_requests/blocking_merge_request_body.vue',
|
||||
'ee/app/assets/javascripts/vue_merge_request_widget/components/blocking_merge_requests/blocking_merge_requests_report.vue',
|
||||
'ee/app/assets/javascripts/vue_merge_request_widget/components/checks/not_approved.vue',
|
||||
|
|
|
|||
|
|
@ -1814,6 +1814,9 @@ ee/app/workers/ai/repository_xray/
|
|||
/ee/lib/sidebars/
|
||||
/ee/lib/ee/sidebars/
|
||||
|
||||
^[Foundations::Design System] @gitlab-org/foundations/design-system/engineering
|
||||
/app/components/pajamas/
|
||||
|
||||
# Necessary for availability, similar to DB migrations
|
||||
[Global Search] @gitlab-org/search-team/migration-maintainers
|
||||
/ee/elastic/migrate/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
include:
|
||||
- component: ${CI_SERVER_FQDN}/gitlab-org/components/danger-review/danger-review@2.0.0
|
||||
- component: ${CI_SERVER_FQDN}/gitlab-org/components/danger-review/danger-review@2.1.0
|
||||
inputs:
|
||||
job_image: "${DEFAULT_CI_IMAGE}"
|
||||
job_stage: "preflight"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ include:
|
|||
inputs:
|
||||
cng_path: 'charts/components/images'
|
||||
- project: 'gitlab-org/quality/pipeline-common'
|
||||
ref: '9.14.0'
|
||||
ref: '10.0.0'
|
||||
file: ci/base.gitlab-ci.yml
|
||||
|
||||
stages:
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ workflow:
|
|||
- .e2e-test-base
|
||||
- .cng-qa-cache # cng-cache includes additional cached helm chart
|
||||
needs:
|
||||
- build-cng-env
|
||||
- build-cng
|
||||
- job: build-cng-env
|
||||
- job: build-cng
|
||||
- pipeline: $PARENT_PIPELINE_ID
|
||||
job: clone-gitlab-repo
|
||||
tags:
|
||||
- e2e
|
||||
variables:
|
||||
|
|
@ -51,6 +53,7 @@ workflow:
|
|||
QA_RUN_IN_PARALLEL: "true"
|
||||
QA_PARALLEL_PROCESSES: 4
|
||||
GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN
|
||||
GIT_STRATEGY: "none"
|
||||
before_script:
|
||||
- !reference [.qa-install, before_script]
|
||||
- !reference [.cng-deploy-cmd, script]
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ workflow:
|
|||
command: [gdk, tail, rails-web]
|
||||
tags:
|
||||
- e2e
|
||||
needs:
|
||||
- pipeline: $PARENT_PIPELINE_ID
|
||||
job: clone-gitlab-repo
|
||||
variables:
|
||||
COVERBAND_ENABLED: "$COVERBAND_ENABLED" # explicitly define variable so it is passed in to gdk service container
|
||||
GITLAB_CRON_JOBS_POLL_INTERVAL: "0"
|
||||
|
|
@ -57,6 +60,7 @@ workflow:
|
|||
QA_DOCKER_NETWORK: host
|
||||
QA_GITLAB_URL: http://gdk.test:3000
|
||||
RSPEC_LAST_RUN_RESULTS_FILE: "$CI_PROJECT_DIR/qa/tmp/examples.txt"
|
||||
GIT_STRATEGY: "none"
|
||||
after_script:
|
||||
- !reference [.with-gdk-log, after_script]
|
||||
- !reference [.gitlab-qa-report, after_script]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
include:
|
||||
- project: gitlab-org/quality/pipeline-common
|
||||
ref: 9.14.0
|
||||
ref: 10.0.0
|
||||
file:
|
||||
- /ci/base.gitlab-ci.yml
|
||||
|
||||
|
|
|
|||
|
|
@ -22,25 +22,3 @@ Layout/FirstHashElementIndentation:
|
|||
- 'spec/requests/api/releases_spec.rb'
|
||||
- 'spec/requests/api/task_completion_status_spec.rb'
|
||||
- 'spec/requests/pwa_controller_spec.rb'
|
||||
- 'spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb'
|
||||
- 'spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb'
|
||||
- 'spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb'
|
||||
- 'spec/rubocop/cop/usage_data/large_table_spec.rb'
|
||||
- 'spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb'
|
||||
- 'spec/services/clusters/update_service_spec.rb'
|
||||
- 'spec/services/notes/render_service_spec.rb'
|
||||
- 'spec/services/projects/container_repository/delete_tags_service_spec.rb'
|
||||
- 'spec/services/projects/create_from_template_service_spec.rb'
|
||||
- 'spec/services/quick_actions/interpret_service_spec.rb'
|
||||
- 'spec/services/service_ping/submit_service_ping_service_spec.rb'
|
||||
- 'spec/sidekiq_cluster/sidekiq_cluster_spec.rb'
|
||||
- 'spec/spam/concerns/has_spam_action_response_fields_spec.rb'
|
||||
- 'spec/support/helpers/kubernetes_helpers.rb'
|
||||
- 'spec/support/helpers/wiki_helpers.rb'
|
||||
- 'spec/support/shared_contexts/lib/container_registry/client_shared_context.rb'
|
||||
- 'spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/harbor/tags_controller_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/models/clusters/prometheus_client_shared.rb'
|
||||
- 'spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb'
|
||||
- 'spec/views/layouts/_head.html.haml_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7b808f014c602c9c308353cde9e2748a44aaf888
|
||||
14cc6838b1a5bf20345484f5cb1171a0aa8d1f49
|
||||
|
|
|
|||
|
|
@ -291,9 +291,6 @@ export default {
|
|||
<paginated-table-with-search-and-tabs
|
||||
:show-error-msg="showErrorMsg"
|
||||
:i18n="$options.i18n"
|
||||
:items="
|
||||
alerts.list || [] /* eslint-disable-line @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:page-info="alerts.pageInfo"
|
||||
:items-count="alertsCount"
|
||||
:status-tabs="$options.statusTabs"
|
||||
|
|
|
|||
|
|
@ -19,11 +19,8 @@ export default {
|
|||
SmartVirtualList,
|
||||
ReportItem,
|
||||
},
|
||||
// Typical height of a report item in px
|
||||
typicalReportItemHeight: 32,
|
||||
/*
|
||||
The maximum amount of shown issues. This is calculated by
|
||||
( max-height of report-block-list / typicalReportItemHeight ) + some safety margin
|
||||
The maximum amount of shown issues.
|
||||
We will use VirtualList if we have more items than this number.
|
||||
For entries lower than this number, the virtual scroll list calculates the total height of the element wrongly.
|
||||
*/
|
||||
|
|
@ -101,7 +98,6 @@ export default {
|
|||
<smart-virtual-list
|
||||
:length="issuesWithState.length"
|
||||
:remain="$options.maxShownReportItems"
|
||||
:size="$options.typicalReportItemHeight"
|
||||
class="report-block-container"
|
||||
:class="listClasses"
|
||||
wtag="ul"
|
||||
|
|
|
|||
|
|
@ -186,7 +186,6 @@ export default {
|
|||
:show-items="true"
|
||||
:show-error-msg="error"
|
||||
:i18n="$options.i18n"
|
||||
:items="contacts.list"
|
||||
:page-info="contacts.pageInfo"
|
||||
:items-count="contactsCount"
|
||||
:status-tabs="$options.statusTabs"
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ export default {
|
|||
:show-items="true"
|
||||
:show-error-msg="error"
|
||||
:i18n="$options.i18n"
|
||||
:items="organizations.list"
|
||||
:page-info="organizations.pageInfo"
|
||||
:items-count="organizationsCount"
|
||||
:status-tabs="$options.statusTabs"
|
||||
|
|
|
|||
|
|
@ -994,13 +994,15 @@ export function rereadNoteHash() {
|
|||
export function setCurrentDiffFileIdFromNote(noteId) {
|
||||
const note = useNotes().notesById[noteId];
|
||||
|
||||
if (!note) return;
|
||||
if (!note) return Promise.resolve();
|
||||
|
||||
const fileHash = useNotes().getDiscussion(note.discussion_id).diff_file?.file_hash;
|
||||
|
||||
if (fileHash && this.flatBlobsList.some((f) => f.fileHash === fileHash)) {
|
||||
this[types.SET_CURRENT_DIFF_FILE](fileHash);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function navigateToDiffFileIndex(index) {
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"PendingGroupMember",
|
||||
"PendingProjectMember"
|
||||
],
|
||||
"ProjectInterface": [
|
||||
"Project",
|
||||
"ProjectMinimalAccess"
|
||||
],
|
||||
"Registrable": [
|
||||
"CiSecureFileRegistry",
|
||||
"ContainerRepositoryRegistry",
|
||||
|
|
|
|||
|
|
@ -344,9 +344,6 @@ export default {
|
|||
:show-items="showList"
|
||||
:show-error-msg="showErrorMsg"
|
||||
:i18n="$options.i18n"
|
||||
:items="
|
||||
incidents.list || [] /* eslint-disable-line @gitlab/vue-no-new-non-primitive-in-template */
|
||||
"
|
||||
:page-info="incidents.pageInfo"
|
||||
:items-count="incidentsCount"
|
||||
:status-tabs="$options.statusTabs"
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
<script>
|
||||
import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
|
||||
import ListItem from '~/vue_shared/components/registry/list_item.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
export default {
|
||||
name: 'MlCandidateListRow',
|
||||
components: {
|
||||
ListItem,
|
||||
GlLink,
|
||||
GlTruncate,
|
||||
GlSprintf,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
props: {
|
||||
candidate: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pathToDetails() {
|
||||
return this.candidate._links?.showPath;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<list-item v-bind="$attrs">
|
||||
<template #left-primary>
|
||||
<div class="gl-flex gl-items-center">
|
||||
<gl-link class="gl-text-default" :href="pathToDetails">
|
||||
<gl-truncate :text="candidate.name" />
|
||||
</gl-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #left-secondary>
|
||||
<span>
|
||||
<gl-sprintf :message="__('Created %{timestamp}')">
|
||||
<template #timestamp>
|
||||
<time-ago-tooltip :time="candidate.createdAt" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</span>
|
||||
</template>
|
||||
</list-item>
|
||||
</template>
|
||||
|
|
@ -61,11 +61,6 @@ export default {
|
|||
default: true,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
isImageLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
@ -265,7 +260,6 @@ export default {
|
|||
:first="first"
|
||||
:selected="isSelected(item)"
|
||||
:is-mobile="isMobile"
|
||||
:disabled="disabled"
|
||||
@select="selectItem(item)"
|
||||
@delete="deleteTags([item])"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<list-item v-bind="$attrs" :selected="selected" :disabled="disabled">
|
||||
<list-item v-bind="$attrs" :selected="selected">
|
||||
<template #left-action>
|
||||
<gl-form-checkbox
|
||||
v-if="tag.userPermissions.destroyContainerRepositoryTag"
|
||||
|
|
|
|||
|
|
@ -177,7 +177,6 @@ export default {
|
|||
:id="$route.params.id"
|
||||
:is-image-loading="isLoading"
|
||||
:is-mobile="isMobile"
|
||||
:disabled="pageActionsAreDisabled"
|
||||
@delete="showAlert"
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<list-item :disabled="isErrorStatus">
|
||||
<list-item>
|
||||
<template #left-primary>
|
||||
<span :class="disabledRowStyle">{{ name }}</span>
|
||||
<clipboard-button
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<list-item data-testid="package-row" :disabled="disabledRow">
|
||||
<list-item data-testid="package-row">
|
||||
<template #left-primary>
|
||||
<div class="gl-mr-3 gl-flex gl-min-w-0 gl-items-center">
|
||||
<gl-link
|
||||
|
|
|
|||
|
|
@ -84,14 +84,11 @@ export default {
|
|||
closeDrawer() {
|
||||
this.open = false;
|
||||
},
|
||||
// eslint-disable-next-line vue/no-unused-properties -- toggleDrawer() is part of the component's public API.
|
||||
toggleDrawer() {
|
||||
this.getDrawerTop();
|
||||
this.open = !this.open;
|
||||
},
|
||||
openDrawer() {
|
||||
this.getDrawerTop();
|
||||
this.open = true;
|
||||
},
|
||||
},
|
||||
safeHtmlConfig: {
|
||||
ADD_TAGS: ['copy-code'],
|
||||
|
|
|
|||
|
|
@ -128,7 +128,6 @@ export default {
|
|||
fullPath: this.projectPath,
|
||||
isLoading: false,
|
||||
isLoadingDraft: false,
|
||||
isLoadingClipboard: false,
|
||||
isReportAbuseDrawerOpen: false,
|
||||
isDropdownVisible: false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ export const initNewResourceDropdown = (props = {}) => {
|
|||
render(createElement) {
|
||||
return createElement(NewResourceDropdown, {
|
||||
props: {
|
||||
withLocalStorage: true,
|
||||
groupId,
|
||||
queryVariables: {
|
||||
...(fullPath
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import { createAlert } from '~/alert';
|
|||
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import AccessorUtilities from '~/lib/utils/accessor';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import searchUserProjectsWithIssuesEnabled from './graphql/search_user_projects_with_issues_enabled.query.graphql';
|
||||
import { RESOURCE_TYPE_ISSUE, RESOURCE_TYPES, RESOURCE_OPTIONS } from './constants';
|
||||
|
|
@ -55,11 +54,6 @@ export default {
|
|||
required: false,
|
||||
default: (data) => data?.projects?.nodes,
|
||||
},
|
||||
withLocalStorage: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -125,9 +119,6 @@ export default {
|
|||
showNoSearchResultsText() {
|
||||
return !this.projects.length && this.search;
|
||||
},
|
||||
canUseLocalStorage() {
|
||||
return this.withLocalStorage && AccessorUtilities.canUseLocalStorage();
|
||||
},
|
||||
selectedProjectForLocalStorage() {
|
||||
const { webUrl, name } = this.selectedProject;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,10 +44,6 @@ export default {
|
|||
},
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
itemsCount: {
|
||||
type: Object,
|
||||
required: false,
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@ export default {
|
|||
default: false,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ export default {
|
|||
name: 'SmartVirtualList',
|
||||
components: { VirtualList },
|
||||
props: {
|
||||
size: { type: Number, required: true },
|
||||
length: { type: Number, required: true },
|
||||
remain: { type: Number, required: true },
|
||||
rtag: { type: String, default: 'div', required: false },
|
||||
|
|
|
|||
|
|
@ -103,9 +103,6 @@ export default {
|
|||
class: this.displayAsCard ? 'gl-mb-3' : 'gl-mr-3',
|
||||
};
|
||||
},
|
||||
validMimeTypeString() {
|
||||
return this.validFileMimetypes.join();
|
||||
},
|
||||
showDropzoneOverlay() {
|
||||
if (this.validateDesignUploadOnDragover && this.acceptDesignFormats) {
|
||||
return this.dragging && this.isDragDataValid && !this.enableDragBehavior;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
class RunnerOwnerProjectResolver < BaseResolver
|
||||
include LooksAhead
|
||||
|
||||
type Types::ProjectType, null: true
|
||||
type ::Types::Projects::ProjectInterface, null: true
|
||||
|
||||
alias_method :runner, :object
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ module Types
|
|||
resolver: Resolvers::Ci::RunnerManagersResolver
|
||||
field :maximum_timeout, GraphQL::Types::Int, null: true,
|
||||
description: 'Maximum timeout (in seconds) for jobs processed by the runner.'
|
||||
field :owner_project, ::Types::ProjectType, null: true,
|
||||
field :owner_project, ::Types::Projects::ProjectInterface, null: true,
|
||||
description: 'Project that owns the runner. For project runners only.',
|
||||
resolver: ::Resolvers::Ci::RunnerOwnerProjectResolver
|
||||
field :paused, GraphQL::Types::Boolean, null: false,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ module Types
|
|||
expose_permissions Types::PermissionTypes::Project
|
||||
|
||||
implements Types::TodoableInterface
|
||||
implements ::Types::Projects::ProjectInterface
|
||||
|
||||
field :id, GraphQL::Types::ID,
|
||||
null: false,
|
||||
|
|
@ -75,11 +76,11 @@ module Types
|
|||
|
||||
field :name, GraphQL::Types::String,
|
||||
null: false,
|
||||
description: 'Name of the project (without namespace).'
|
||||
description: 'Name of the project without the namespace.'
|
||||
|
||||
field :name_with_namespace, GraphQL::Types::String,
|
||||
null: false,
|
||||
description: 'Full name of the project with its namespace.'
|
||||
description: 'Name of the project including the namespace.'
|
||||
|
||||
field :description, GraphQL::Types::String,
|
||||
null: true,
|
||||
|
|
@ -165,7 +166,7 @@ module Types
|
|||
field :avatar_url, GraphQL::Types::String,
|
||||
null: true,
|
||||
calls_gitaly: true,
|
||||
description: 'URL to avatar image file of the project.'
|
||||
description: 'Avatar URL of the project.'
|
||||
|
||||
field :jobs_enabled, GraphQL::Types::Boolean,
|
||||
null: true,
|
||||
|
|
@ -880,10 +881,6 @@ module Types
|
|||
|
||||
markdown_field :description_html, null: true
|
||||
|
||||
def avatar_url
|
||||
object.avatar_url(only_path: false)
|
||||
end
|
||||
|
||||
def jobs_enabled
|
||||
object.feature_available?(:builds, context[:current_user])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Projects
|
||||
# rubocop: disable GraphQL/GraphqlName -- Not a type
|
||||
# rubocop: disable Graphql/AuthorizeTypes -- Not a type
|
||||
class ProjectBaseField < ::Types::BaseField
|
||||
def initialize(**kwargs, &block)
|
||||
kwargs[:authorize] = :read_project
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
# rubocop: enable Graphql/AuthorizeTypes
|
||||
# rubocop: enable GraphQL/GraphqlName
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Projects
|
||||
# This inteface sets [authorize: :read_project] (field-level authorization via
|
||||
# ProjectBaseField) for all defined fields to ensure implementing types don't
|
||||
# expose inherited fields without proper authorization.
|
||||
#
|
||||
# Implementing types can opt-out from this field-level auth and use
|
||||
# type-level auth by re-defining the field without the authorize argument.
|
||||
# For example, ProjectType uses :read_project type-level auth and redefines all
|
||||
# fields in this interface to opt-out while ProjectMinimalAccessType uses
|
||||
# :read_project_metadata type-level auth to expose a set of defined fields and
|
||||
# leaves inherited fields it does not want to expose to use field-level auth
|
||||
# using :read_project.
|
||||
module ProjectInterface
|
||||
include BaseInterface
|
||||
|
||||
connection_type_class Types::CountableConnectionType
|
||||
|
||||
graphql_name 'ProjectInterface'
|
||||
|
||||
field_class ::Types::Projects::ProjectBaseField
|
||||
|
||||
field :avatar_url, GraphQL::Types::String,
|
||||
null: true,
|
||||
calls_gitaly: true,
|
||||
description: 'Avatar URL of the project.'
|
||||
field :description, GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Short description of the project.'
|
||||
field :id, GraphQL::Types::ID, null: true,
|
||||
description: 'ID of the project.'
|
||||
field :name, GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Name of the project without the namespace.'
|
||||
field :name_with_namespace, GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Name of the project including the namespace.'
|
||||
field :web_url, GraphQL::Types::String,
|
||||
null: true,
|
||||
description: 'Web URL of the project.'
|
||||
|
||||
def self.resolve_type(_object, _context)
|
||||
::Types::ProjectType
|
||||
end
|
||||
|
||||
def avatar_url
|
||||
object.avatar_url(only_path: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::Projects::ProjectInterface.prepend_mod
|
||||
|
|
@ -1059,6 +1059,7 @@ class ProjectPolicy < BasePolicy
|
|||
|
||||
rule { can?(:read_project) }.policy do
|
||||
enable :read_incident_management_timeline_event_tag
|
||||
enable :read_project_metadata
|
||||
end
|
||||
|
||||
rule { can?(:download_code) }.policy do
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
- add_page_specific_style 'page_bundles/members'
|
||||
- page_title _('Group members')
|
||||
- content_for :reload_on_member_invite_success, true
|
||||
- content_for :hide_invite_members_button, true
|
||||
|
||||
- if can_admin_group_member?(@group)
|
||||
= render_if_exists 'shared/promotions/promote_planner_role'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
- page_title _("Members")
|
||||
- show_the_header = can_invite_members_for_project?(@project) || project_can_be_shared?
|
||||
- content_for :reload_on_member_invite_success, true
|
||||
- content_for :hide_invite_members_button, true
|
||||
|
||||
= render_if_exists 'shared/promotions/promote_planner_role'
|
||||
- if show_the_header
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@
|
|||
- 1
|
||||
- - analytics_code_review_metrics
|
||||
- 1
|
||||
- - analytics_code_suggestions_usage_backfill
|
||||
- - analytics_code_suggestions_events_backfill
|
||||
- 1
|
||||
- - analytics_devops_adoption_create_snapshot
|
||||
- 1
|
||||
- - analytics_duo_chat_events_backfill
|
||||
- 1
|
||||
- - analytics_usage_trends_counter_job
|
||||
- 1
|
||||
- - anti_abuse_ban_duplicate_users
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
- title: "Default GitLab Runner's `FF_GIT_URLS_WITHOUT_TOKENS` feature flag to `true`"
|
||||
removal_milestone: "18.0"
|
||||
announcement_milestone: "17.9"
|
||||
breaking_change: true
|
||||
# window: # Can be 1, 2, or 3 - The window when the breaking change will be deployed on GitLab.com
|
||||
reporter: hoegaarden
|
||||
stage: stage
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/518709
|
||||
# Use the impact calculator https://gitlab-com.gitlab.io/gl-infra/breaking-change-impact-calculator/?
|
||||
impact: medium # Can be one of: [critical, high, medium, low]
|
||||
# scope: # Can be one or a combination of: [instance, group, project]
|
||||
resolution_role: Owner # Can be one of: [Admin, Owner, Maintainer, Developer]
|
||||
manual_task: false # Can be true or false. Use this to denote whether a resolution action must be performed manually (true), or if it can be automated by using the API or other automation (false).
|
||||
body: |
|
||||
In GitLab Runner 18.0, to limit the potential for token leakage, the
|
||||
default value for the `FF_GIT_URLS_WITHOUT_TOKENS` feature flag changes
|
||||
to `true`.
|
||||
|
||||
This change affects users who:
|
||||
|
||||
- Use executors that share Git credential state across jobs (for example, shell executor).
|
||||
- Have a caching Git credential helper installed (for example,
|
||||
[gitforwindows](https://gitforwindows.org/) installs
|
||||
[Git credential manager (GCM)](https://github.com/git-ecosystem/git-credential-manager)
|
||||
system-wide by default).
|
||||
- Run builds in parallel.
|
||||
|
||||
To prevent issues, ensure that you don't use any caching Git credential
|
||||
helper with GitLab Runner, use an executor which runs jobs in isolated
|
||||
environments, or run job serially only.
|
||||
# # ==============================
|
||||
# # OPTIONAL END-OF-SUPPORT FIELDS
|
||||
# # ==============================
|
||||
# #
|
||||
# # If an End of Support period applies:
|
||||
# # 1) Share this announcement in the `#spt_managers` Support channel in Slack
|
||||
# # 2) Mention `@gitlab-com/support` in this merge request.
|
||||
# #
|
||||
# # When support for this feature ends, in XX.YY milestone format.
|
||||
# end_of_support_milestone:
|
||||
# # Array of tiers the feature is currently available to,
|
||||
# # like [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
# tiers:
|
||||
# # Links to documentation and thumbnail image
|
||||
# documentation_url:
|
||||
# image_url:
|
||||
# # Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
||||
# video_url:
|
||||
|
|
@ -540,7 +540,7 @@ W, [2022-11-28T13:14:09.296911 #10025] WARN -- : Failed to transfer Ci::JobArti
|
|||
|
||||
Attempting to [delete references to missing artifacts](check.md#delete-references-to-missing-artifacts) after you have disabled object storage, results in the following error:
|
||||
|
||||
```shell
|
||||
```plaintext
|
||||
RuntimeError (Object Storage is not enabled for JobArtifactUploader)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -22808,7 +22808,7 @@ CI/CD variables for a project.
|
|||
| <a id="cirunnermaintenancenote"></a>`maintenanceNote` | [`String`](#string) | Runner's maintenance notes. |
|
||||
| <a id="cirunnermaintenancenotehtml"></a>`maintenanceNoteHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `maintenance_note`. |
|
||||
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
|
||||
| <a id="cirunnerownerproject"></a>`ownerProject` | [`Project`](#project) | Project that owns the runner. For project runners only. |
|
||||
| <a id="cirunnerownerproject"></a>`ownerProject` | [`ProjectInterface`](#projectinterface) | Project that owns the runner. For project runners only. |
|
||||
| <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. |
|
||||
| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "compute cost factor" associated with the runner (GitLab.com only). |
|
||||
| <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. |
|
||||
|
|
@ -34399,7 +34399,7 @@ Project-level settings for product analytics provider.
|
|||
| <a id="projectapifuzzingciconfiguration"></a>`apiFuzzingCiConfiguration` | [`ApiFuzzingCiConfiguration`](#apifuzzingciconfiguration) | API fuzzing configuration for the project. |
|
||||
| <a id="projectarchived"></a>`archived` | [`Boolean`](#boolean) | Indicates the archived status of the project. |
|
||||
| <a id="projectautoclosereferencedissues"></a>`autocloseReferencedIssues` | [`Boolean`](#boolean) | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically. |
|
||||
| <a id="projectavatarurl"></a>`avatarUrl` | [`String`](#string) | URL to avatar image file of the project. |
|
||||
| <a id="projectavatarurl"></a>`avatarUrl` | [`String`](#string) | Avatar URL of the project. |
|
||||
| <a id="projectciaccessauthorizedagents"></a>`ciAccessAuthorizedAgents` | [`ClusterAgentAuthorizationCiAccessConnection`](#clusteragentauthorizationciaccessconnection) | Authorized cluster agents for the project through ci_access keyword. (see [Connections](#connections)) |
|
||||
| <a id="projectcicdsettings"></a>`ciCdSettings` | [`ProjectCiCdSetting`](#projectcicdsetting) | CI/CD settings for the project. |
|
||||
| <a id="projectciconfigpathordefault"></a>`ciConfigPathOrDefault` | [`String!`](#string) | Path of the CI configuration file. |
|
||||
|
|
@ -34462,8 +34462,8 @@ Project-level settings for product analytics provider.
|
|||
| <a id="projectmergerequestsdisablecommittersapproval"></a>`mergeRequestsDisableCommittersApproval` | [`Boolean!`](#boolean) | Indicates that committers of the given merge request cannot approve. |
|
||||
| <a id="projectmergerequestsenabled"></a>`mergeRequestsEnabled` | [`Boolean`](#boolean) | Indicates if Merge requests are enabled for the current user. |
|
||||
| <a id="projectmergerequestsffonlyenabled"></a>`mergeRequestsFfOnlyEnabled` | [`Boolean`](#boolean) | Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. |
|
||||
| <a id="projectname"></a>`name` | [`String!`](#string) | Name of the project (without namespace). |
|
||||
| <a id="projectnamewithnamespace"></a>`nameWithNamespace` | [`String!`](#string) | Full name of the project with its namespace. |
|
||||
| <a id="projectname"></a>`name` | [`String!`](#string) | Name of the project without the namespace. |
|
||||
| <a id="projectnamewithnamespace"></a>`nameWithNamespace` | [`String!`](#string) | Name of the project including the namespace. |
|
||||
| <a id="projectnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace of the project. |
|
||||
| <a id="projectonlyallowmergeifalldiscussionsareresolved"></a>`onlyAllowMergeIfAllDiscussionsAreResolved` | [`Boolean`](#boolean) | Indicates if merge requests of the project can only be merged when all the discussions are resolved. |
|
||||
| <a id="projectonlyallowmergeifallstatuscheckspassed"></a>`onlyAllowMergeIfAllStatusChecksPassed` | [`Boolean`](#boolean) | Indicates that merges of merge requests should be blocked unless all status checks have passed. |
|
||||
|
|
@ -36805,6 +36805,19 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="projectmembermergerequestinteractionid"></a>`id` | [`MergeRequestID!`](#mergerequestid) | Global ID of the merge request. |
|
||||
|
||||
### `ProjectMinimalAccess`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectminimalaccessavatarurl"></a>`avatarUrl` | [`String`](#string) | Avatar URL of the project. |
|
||||
| <a id="projectminimalaccessdescription"></a>`description` | [`String`](#string) | Short description of the project. |
|
||||
| <a id="projectminimalaccessid"></a>`id` | [`ID`](#id) | ID of the project. |
|
||||
| <a id="projectminimalaccessname"></a>`name` | [`String!`](#string) | Name of the project without the namespace. |
|
||||
| <a id="projectminimalaccessnamewithnamespace"></a>`nameWithNamespace` | [`String!`](#string) | Name of the project including the namespace. |
|
||||
| <a id="projectminimalaccessweburl"></a>`webUrl` | [`String`](#string) | Web URL of the project. |
|
||||
|
||||
### `ProjectPermissions`
|
||||
|
||||
#### Fields
|
||||
|
|
@ -47252,6 +47265,24 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="pendingmemberinterfacemergerequestinteractionid"></a>`id` | [`MergeRequestID!`](#mergerequestid) | Global ID of the merge request. |
|
||||
|
||||
#### `ProjectInterface`
|
||||
|
||||
Implementations:
|
||||
|
||||
- [`Project`](#project)
|
||||
- [`ProjectMinimalAccess`](#projectminimalaccess)
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectinterfaceavatarurl"></a>`avatarUrl` | [`String`](#string) | Avatar URL of the project. |
|
||||
| <a id="projectinterfacedescription"></a>`description` | [`String`](#string) | Short description of the project. |
|
||||
| <a id="projectinterfaceid"></a>`id` | [`ID`](#id) | ID of the project. |
|
||||
| <a id="projectinterfacename"></a>`name` | [`String`](#string) | Name of the project without the namespace. |
|
||||
| <a id="projectinterfacenamewithnamespace"></a>`nameWithNamespace` | [`String`](#string) | Name of the project including the namespace. |
|
||||
| <a id="projectinterfaceweburl"></a>`webUrl` | [`String`](#string) | Web URL of the project. |
|
||||
|
||||
#### `ResolvableInterface`
|
||||
|
||||
Implementations:
|
||||
|
|
|
|||
|
|
@ -47,8 +47,11 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --data "name=mytoken" --data "expires_at=2017-04-04" \
|
||||
--data "scopes[]=api" "https://gitlab.example.com/api/v4/users/42/personal_access_tokens"
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--data "name=mytoken" --data "expires_at=2017-04-04" \
|
||||
--data "scopes[]=api" \
|
||||
--url "https://gitlab.example.com/api/v4/users/42/personal_access_tokens"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
@ -106,7 +109,10 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --data "name=mytoken" --data "scopes[]=k8s_proxy" "https://gitlab.example.com/api/v4/user/personal_access_tokens"
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--data "name=mytoken" --data "scopes[]=k8s_proxy" \
|
||||
--url "https://gitlab.example.com/api/v4/user/personal_access_tokens"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
@ -152,7 +158,9 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/users/42/impersonation_tokens"
|
||||
curl --request GET \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--url "https://gitlab.example.com/api/v4/users/42/impersonation_tokens"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
@ -214,7 +222,9 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/2"
|
||||
curl --request GET \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--url "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/2"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
@ -263,8 +273,11 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --data "name=mytoken" --data "expires_at=2017-04-04" \
|
||||
--data "scopes[]=api" "https://gitlab.example.com/api/v4/users/42/impersonation_tokens"
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--data "name=mytoken" --data "expires_at=2017-04-04" \
|
||||
--data "scopes[]=api" \
|
||||
--url "https://gitlab.example.com/api/v4/users/42/impersonation_tokens"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
|
@ -309,5 +322,7 @@ Supported attributes:
|
|||
Example request:
|
||||
|
||||
```shell
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/1"
|
||||
curl --request DELETE \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--url "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/1"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
---
|
||||
stage: Security Risk Management
|
||||
group: Security Infrastructure
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
title: Vulnerability archive export API
|
||||
---
|
||||
|
||||
{{< details >}}
|
||||
|
||||
- Tier: Ultimate
|
||||
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
|
||||
- Status: Beta
|
||||
|
||||
{{< /details >}}
|
||||
|
||||
Every API call to vulnerability archive exports must be [authenticated](rest/authentication.md).
|
||||
|
||||
## Export archived vulnerabilities for a project
|
||||
|
||||
Creates a new export for a project.
|
||||
|
||||
If an authenticated user doesn't have permission to
|
||||
[read vulnerabilities](../user/permissions.md#project-members-permissions),
|
||||
this request returns a `403 Forbidden` status code.
|
||||
|
||||
Exports are retained for 1 month after they are created.
|
||||
|
||||
```plaintext
|
||||
POST /security/projects/:id/vulnerability_archive_exports
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|-------------------|----------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path](rest/_index.md#namespaced-paths) of the project that the authenticated user is a member of. |
|
||||
| `start_date` | string | yes | Date in `dd/mm/yyyy` format. Vulnerabilities that were archived on or after this date are included in the export. |
|
||||
| `end_date` | string | yes | Date in `dd/mm/yyyy` format. Vulnerabilities that were archived on or before this date are included in the export. |
|
||||
| `export_format` | string | no | Format of the export. The default and only valid value is `csv`. |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/security/projects/1/vulnerability_archive_exports"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 2,
|
||||
"created_at": "2020-03-30T09:35:38.746Z",
|
||||
"project_id": 1,
|
||||
"format": "csv",
|
||||
"status": "created",
|
||||
"started_at": null,
|
||||
"finished_at": null,
|
||||
"_links": {
|
||||
"self": "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2",
|
||||
"download": "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2/download"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Get single vulnerability archive export
|
||||
|
||||
Get the attributes for an existing export.
|
||||
You can use this endpoint to poll the export status until it is `finished`.
|
||||
Clients should use the `Poll-Interval` response header to determine how long
|
||||
to wait before sending the next poll.
|
||||
The `Poll-Interval` is a number representing the time in milliseconds.
|
||||
|
||||
Exports can have these status values:
|
||||
|
||||
| Status | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------- |
|
||||
| `created` | The export has not started running yet. |
|
||||
| `running` | The export is being generated. |
|
||||
| `finished` | The export is now available for download. |
|
||||
| `failed` | There was an error while generating the export and it could not be completed. |
|
||||
| `purged` | The export is queued for deletion and the file is no longer available for download. |
|
||||
|
||||
```plaintext
|
||||
GET /security/vulnerability_archive_exports/:id
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer or string | yes | The vulnerability archive export's ID |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2"
|
||||
```
|
||||
|
||||
If the vulnerability export isn't finished, the response is `202 Accepted`.
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 2,
|
||||
"created_at": "2020-03-30T09:35:38.746Z",
|
||||
"project_id": 1,
|
||||
"format": "csv",
|
||||
"status": "running",
|
||||
"started_at": "2020-03-30T09:38:24Z",
|
||||
"finished_at": null,
|
||||
"_links": {
|
||||
"self": "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2",
|
||||
"download": "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2/download"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Download a vulnerability archive export
|
||||
|
||||
Download the export file for a finished export.
|
||||
Before you call this endpoint, [verify](#get-single-vulnerability-archive-export) that the export status is `finished`.
|
||||
|
||||
```plaintext
|
||||
GET /security/vulnerability_archive_exports/:id/download
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer or string | yes | The vulnerability export's ID |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" --output export.csv "https://gitlab.example.com/api/v4/security/vulnerability_archive_exports/2/download"
|
||||
```
|
||||
|
||||
If the vulnerability export is not finished yet or was not found, the response is `404 Not Found`.
|
||||
|
||||
Example response:
|
||||
|
||||
```plaintext
|
||||
Group Name,Project Name,Tool,Scanner Name,Status,Vulnerability,Details,Additional Info,Severity,CVE,CWE,Other Identifiers,Detected At,Location,Activity,Comments,Full Path,CVSS Vectors,Dismissal Reason
|
||||
Gitlab.org,Defend,container_scanning,Trivy,resolved,CVE-2019-14697 in musl-utils-1.1.20-r4,"musl libc through 1.1.23 has an x87 floating-point stack adjustment imbalance, related to the math/i386/ directory. In some cases, use of this library could introduce out-of-bounds writes that are not present in an application's source code.",CVE-2019-14697 in musl-utils-1.1.20-r4,critical,CVE-2019-14697,,"",2022-10-07 13:34:41 UTC,"{""image""=>""python:3.4-alpine"", ""dependency""=>{""package""=>{""name""=>""musl-utils""}, ""version""=>""1.1.20-r4""}, ""operating_system""=>""alpine 3.9.2""}",true,"2022-10-07 13:41:08 UTC|root|resolved|changed vulnerability status to resolved",group/project/1,,,
|
||||
Gitlab.org,Defend,container_scanning,Trivy,detected,CVE-2019-19242 in sqlite-libs-3.26.0-r3,"SQLite 3.30.1 mishandles pExpr->y.pTab, as demonstrated by the TK_COLUMN case in sqlite3ExprCodeTarget in expr.c.",CVE-2019-19242 in sqlite-libs-3.26.0-r3,medium,CVE-2019-19242,,"",2022-10-07 13:34:41 UTC,"{""image""=>""python:3.4-alpine"", ""dependency""=>{""package""=>{""name""=>""sqlite-libs""}, ""version""=>""3.26.0-r3""}, ""operating_system""=>""alpine 3.9.2""}",true,"",group/project/2,,,
|
||||
Gitlab.org,Defend,container_scanning,Trivy,detected,CVE-2020-28928 in musl-1.1.20-r4,"In musl libc through 1.2.1, wcsnrtombs mishandles particular combinations of destination buffer size and source character limit, as demonstrated by an invalid write access (buffer overflow).",CVE-2020-28928 in musl-1.1.20-r4,medium,CVE-2020-28928,,"",2022-10-07 13:34:41 UTC,"{""image""=>""python:3.4-alpine"", ""dependency""=>{""package""=>{""name""=>""musl""}, ""version""=>""1.1.20-r4""}, ""operating_system""=>""alpine 3.9.2""}",true,"",group/project/3,,,
|
||||
Gitlab.org,Defend,dependency_scanning,Gemnasium,detected,Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') in rack,Carefully crafted requests can cause shell escape sequences to be written to the terminal via Rack's Lint middleware and CommonLogger middleware. These escape sequences can be leveraged to possibly execute commands in the victim's terminal.,Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') in rack,unknown,Gemfile.lock:rack:gemnasium:60b5a27f-4e4d-4ab4-8ae7-74b4b212e177,,Gemnasium-60b5a27f-4e4d-4ab4-8ae7-74b4b212e177; GHSA-wq4h-7r42-5hrr,2022-10-14 13:16:00 UTC,"{""file""=>""Gemfile.lock"", ""dependency""=>{""package""=>{""name""=>""rack""}, ""version""=>""2.2.3""}}",false,group/project/4,,,
|
||||
Gitlab.org,Defend,dependency_scanning,Gemnasium,detected,Denial of Service Vulnerability in Rack Multipart Parsing in rack,"Carefully crafted multipart POST requests can cause Rack's multipart parser to take much longer than expected, leading to a possible denial of service vulnerability. Impacted code will use Rack's multipart parser to parse multipart posts.",Denial of Service Vulnerability in Rack Multipart Parsing in rack,unknown,Gemfile.lock:rack:gemnasium:20daa17a-47b5-4f79-80c2-cd8f2db9805c,,Gemnasium-20daa17a-47b5-4f79-80c2-cd8f2db9805c; GHSA-hxqx-xwvh-44m2,2022-10-14 13:16:00 UTC,"{""file""=>""Gemfile.lock"", ""dependency""=>{""package""=>{""name""=>""rack""}, ""version""=>""2.2.3""}}",false,group/project/5,,,
|
||||
Gitlab.org,Defend,sast,Brakeman,detected,Possible SQL injection,,Possible SQL injection,medium,e52f23a259cd489168b4313317ac94a3f13bffde57b9635171c1a44a9f329e9a,,"""Brakeman Warning Code 0""",2022-10-13 15:16:36 UTC,"{""file""=>""main.rb"", ""class""=>""User"", ""method""=>""index"", ""start_line""=>3}",false,"",group/project/6,,,
|
||||
Gitlab.org,Defend,sast,Semgrep,dismissed,Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'),"SQL Injection is a critical vulnerability that can lead to data or system compromise...",,critical,,CWE-89,SCS0002,2023-12-28 10:48:34 UTC,"{""file""=>""WebGoat/App_Code/DB/SqliteDbProvider.cs"", ""start_line""=>274}",false,"2023-12-28 10:51:32 UTC|root|Dismissed|""changed vulnerability status to Dismissed: Not Applicable and the following comment: ""dismiss 5""",gitlab-org/defend/579,,Not applicable,
|
||||
```
|
||||
|
|
@ -117,6 +117,7 @@ where:
|
|||
- [YAML anchors](../yaml/yaml_optimization.md#anchors) are replaced with the linked configuration.
|
||||
- [YAML `!reference` tags](../yaml/yaml_optimization.md#reference-tags) are also replaced
|
||||
with the linked configuration.
|
||||
- Conditional rules are evaluated assuming a default branch push event.
|
||||
|
||||
Using `!reference` tags can cause nested configuration that display with
|
||||
multiple hyphens (`-`) at the start of the line in the expanded view. This behavior is expected, and the extra
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ To see what polyfills are being used:
|
|||
1. In the **Search modules** field, enter `gitlab/node_modules/core-js` to see
|
||||
which polyfills are being loaded and where:
|
||||
|
||||

|
||||

|
||||
|
||||
### 9. Why is my page broken in dark mode?
|
||||
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ Even with an updated history, old commits can still be
|
|||
accessed by commit SHA, at least until all the automated cleanup
|
||||
of detached commits is performed, or a cleanup is run manually. Even the cleanup might not remove old commits if there are still refs pointing to them.
|
||||
|
||||

|
||||

|
||||
|
||||
You should not change the history when you're working in a public branch
|
||||
or a branch that might be used by others.
|
||||
|
|
|
|||
|
|
@ -1014,37 +1014,6 @@ In most cases, the 45-second value was higher than the timeout value of many sca
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### Default GitLab Runner's `FF_GIT_URLS_WITHOUT_TOKENS` feature flag to `true`
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
||||
- Announced in GitLab <span class="milestone">17.9</span>
|
||||
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/update/terminology/#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/518709).
|
||||
|
||||
</div>
|
||||
|
||||
In GitLab Runner 18.0, to limit the potential for token leakage, the
|
||||
default value for the `FF_GIT_URLS_WITHOUT_TOKENS` feature flag changes
|
||||
to `true`.
|
||||
|
||||
This change affects users who:
|
||||
|
||||
- Use executors that share Git credential state across jobs (for example, shell executor).
|
||||
- Have a caching Git credential helper installed (for example,
|
||||
[gitforwindows](https://gitforwindows.org/) installs
|
||||
[Git credential manager (GCM)](https://github.com/git-ecosystem/git-credential-manager)
|
||||
system-wide by default).
|
||||
- Run builds in parallel.
|
||||
|
||||
To prevent issues, ensure that you don't use any caching Git credential
|
||||
helper with GitLab Runner, use an executor which runs jobs in isolated
|
||||
environments, or run job serially only.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### Dependency Proxy token scope enforcement
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ If the [Dependency Scanning](../dependency_scanning/_index.md) CI job is configu
|
|||
|
||||
## Download the dependency list
|
||||
|
||||
You can download the full list of dependencies and their details in JSON or CSV format.
|
||||
You can download the full list of dependencies and their details in JSON, CSV, or CycloneDX format.
|
||||
The dependency list shows only the results of the last successful pipeline that ran on the default branch.
|
||||
|
||||
To download the dependency list:
|
||||
|
|
|
|||
|
|
@ -431,30 +431,27 @@ To configure for GitLab Self-Managed:
|
|||
|
||||
{{< /history >}}
|
||||
|
||||
GitLab administrators can use the global SAML group memberships lock to prevent group members from inviting new members to subgroups that have their membership synchronized with SAML Group Links.
|
||||
You can enforce a global lock on SAML group memberships. This lock limits who can invite new members to subgroups where membership is synchronized with SAML Group Links.
|
||||
|
||||
Global group memberships lock only applies to subgroups of a top-level group where SAML Group Links synchronization is configured. No user can modify the
|
||||
membership of a top-level group configured for SAML Group Links synchronization.
|
||||
When you lock group memberships:
|
||||
|
||||
When global group memberships lock is enabled:
|
||||
|
||||
- Only an administrator can manage memberships of any group including access levels.
|
||||
- Users cannot:
|
||||
- You cannot set a group or subgroup as a [Code Owner](../../project/codeowners/_index.md).
|
||||
For more information, see [Incompatibility with Global SAML group memberships lock](../../project/codeowners/troubleshooting.md#incompatibility-with-global-saml-group-memberships-lock).
|
||||
- Only administrators can manage group members and change their access levels.
|
||||
- Group members cannot:
|
||||
- Share a project with other groups.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
You cannot set groups or subgroups as [Code Owners](../../project/codeowners/_index.md).
|
||||
The Code Owners feature requires direct group memberships, which are not possible when this lock is enabled.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
- Invite members to a project created in a group.
|
||||
- Modify the membership of a top-level group configured for SAML Group Links synchronization.
|
||||
|
||||
To enable global group memberships lock:
|
||||
### Lock group memberships
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [SAML SSO for GitLab Self-Managed](../../../integration/saml.md) must be configured.
|
||||
|
||||
To lock memberships to SAML Group Links synchronization:
|
||||
|
||||
1. [Configure SAML](../../../integration/saml.md) for GitLab Self-Managed.
|
||||
1. On the left sidebar, at the bottom, select **Admin**.
|
||||
1. Select **Settings > General**.
|
||||
1. Expand the **Visibility and access controls** section.
|
||||
1. Ensure that **Lock memberships to SAML Group Links synchronization** is selected.
|
||||
1. Select the **Lock memberships to SAML Group Links synchronization** checkbox.
|
||||
|
|
|
|||
|
|
@ -307,6 +307,13 @@ pages:
|
|||
pages: false
|
||||
```
|
||||
|
||||
{{< alert type="warning" >}}
|
||||
|
||||
If you have multiple Pages jobs in your pipeline with the same value for
|
||||
`path_prefix`, the last one to be completed will be deployed with Pages.
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
## Parallel deployments
|
||||
|
||||
To create multiple deployments for your project at the same time, for example to
|
||||
|
|
|
|||
|
|
@ -230,3 +230,23 @@ The previous YAML example uses [user-defined job names](_index.md#user-defined-j
|
|||
|
||||
Parallel Pages deployments, created by a merge request with a `path_prefix`, are automatically deleted when the
|
||||
merge request is closed or merged.
|
||||
|
||||
### Usage with redirects
|
||||
|
||||
Redirects use absolute paths.
|
||||
Because parallel deployments are available on a sub-path, redirects require
|
||||
additional modifications to the `_redirects` file to work in parallel deployments.
|
||||
|
||||
Existing files always take priority over a redirect rule, so you can use a splat placeholder
|
||||
to catch requests to prefixed paths.
|
||||
|
||||
If your `path_prefix` is `/mr-${$CI_MERGE_REQUEST_IID}`, adapt this `_redirect` file example
|
||||
to redirect requests for both primary and parallel deployments:
|
||||
|
||||
```shell
|
||||
# Redirect the primary deployment
|
||||
/will-redirect.html /redirected.html 302
|
||||
|
||||
# Redirect parallel deployments
|
||||
/*/will-redirect.html /:splat/redirected.html 302
|
||||
```
|
||||
|
|
|
|||
|
|
@ -54860,7 +54860,7 @@ msgstr ""
|
|||
msgid "SecurityReports|Cluster"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Coming soon: retention policies"
|
||||
msgid "SecurityReports|Coming soon: time-based data retention limits"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Comment (required)"
|
||||
|
|
@ -55153,7 +55153,7 @@ msgstr ""
|
|||
msgid "SecurityReports|Sorry, your filter produced no results"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Starting 2025-05-15, vulnerabilities that have not been updated in the last 12 months will automatically move the vulnerability archive for 3 years before deletion. To access the archive, on the left sidebar, select Secure > Security Configuration > Vulnerability Management."
|
||||
msgid "SecurityReports|Starting 2025-05-15, we will gradually migrate vulnerabilities that have not been updated in the last 12 months to the vulnerability archive. Archived vulnerabilities are stored for 3 years before deletion. To access the archive, on the left sidebar, select Secure > Security Configuration > Vulnerability Management."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Status"
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ spec/frontend/issues/list/components/issues_list_app_spec.js
|
|||
spec/frontend/issues/service_desk/components/service_desk_list_app_spec.js
|
||||
spec/frontend/issues/show/components/app_spec.js
|
||||
spec/frontend/issues/show/components/fields/description_spec.js
|
||||
spec/frontend/issues/show/components/header_actions_spec.js
|
||||
spec/frontend/jira_import/components/jira_import_form_spec.js
|
||||
spec/frontend/lib/utils/breadcrumbs_spec.js
|
||||
spec/frontend/lib/utils/confirm_via_gl_modal/confirm_action_spec.js
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ describe('Customer relations contacts root app', () => {
|
|||
mountComponent();
|
||||
|
||||
expect(findTable().props()).toMatchObject({
|
||||
items: [],
|
||||
itemsCount: {},
|
||||
pageInfo: {},
|
||||
statusTabs: [
|
||||
|
|
@ -135,14 +134,5 @@ describe('Customer relations contacts root app', () => {
|
|||
|
||||
expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
|
||||
});
|
||||
|
||||
it('renders correct results', async () => {
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findTable().props('items')).toEqual(
|
||||
getGroupContactsQueryResponse.data.group.contacts.nodes,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ describe('Customer relations organizations root app', () => {
|
|||
mountComponent();
|
||||
|
||||
expect(findTable().props()).toMatchObject({
|
||||
items: [],
|
||||
itemsCount: {},
|
||||
pageInfo: {},
|
||||
statusTabs: [
|
||||
|
|
@ -139,14 +138,5 @@ describe('Customer relations organizations root app', () => {
|
|||
|
||||
expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
|
||||
});
|
||||
|
||||
it('renders correct results', async () => {
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findTable().props('items')).toEqual(
|
||||
getGroupOrganizationsQueryResponse.data.group.organizations.nodes,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -301,14 +301,12 @@ describe('HeaderActions component', () => {
|
|||
|
||||
it(`${isItemVisible ? 'shows' : 'hides'} "${itemText}" item`, () => {
|
||||
expect(
|
||||
findDropdownItems()
|
||||
.filter((item) => {
|
||||
return item.props('item')
|
||||
? item.props('item').text === itemText
|
||||
: item.text() === itemText;
|
||||
})
|
||||
.exists(),
|
||||
).toBe(isItemVisible);
|
||||
findDropdownItems().filter((item) => {
|
||||
return item.props('item')
|
||||
? item.props('item').text === itemText
|
||||
: item.text() === itemText;
|
||||
}),
|
||||
).toHaveLength(isItemVisible ? 1 : 0);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import ListItem from '~/vue_shared/components/registry/list_item.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import CandidateListRow from '~/ml/model_registry/components/candidate_list_row.vue';
|
||||
import { graphqlCandidates } from '../graphql_mock_data';
|
||||
|
||||
const CANDIDATE = graphqlCandidates[0];
|
||||
|
||||
let wrapper;
|
||||
const createWrapper = (candidate = CANDIDATE) => {
|
||||
wrapper = shallowMount(CandidateListRow, {
|
||||
propsData: { candidate },
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
GlTruncate,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findListItem = () => wrapper.findComponent(ListItem);
|
||||
const findLink = () => findListItem().findComponent(GlLink);
|
||||
const findTruncated = () => findLink().findComponent(GlTruncate);
|
||||
const findTooltip = () => findListItem().findComponent(TimeAgoTooltip);
|
||||
|
||||
describe('ml/model_registry/components/candidate_list_row.vue', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
it('Has a link to the candidate', () => {
|
||||
expect(findTruncated().props('text')).toBe(CANDIDATE.name);
|
||||
expect(findLink().attributes('href')).toBe(CANDIDATE._links.showPath);
|
||||
});
|
||||
|
||||
it('Shows created at', () => {
|
||||
expect(findTooltip().props('time')).toBe(CANDIDATE.createdAt);
|
||||
});
|
||||
});
|
||||
|
|
@ -372,10 +372,7 @@ describe('Tags List', () => {
|
|||
|
||||
const rows = findTagsListRow();
|
||||
|
||||
expect(rows.at(0).attributes()).toMatchObject({
|
||||
first: 'true',
|
||||
disabled: 'true',
|
||||
});
|
||||
expect(rows.at(0).attributes('first')).toBe('true');
|
||||
});
|
||||
|
||||
describe('events', () => {
|
||||
|
|
|
|||
|
|
@ -394,7 +394,6 @@ describe('Details Page', () => {
|
|||
await waitForApolloRequestRender();
|
||||
|
||||
expect(findDetailsHeader().props('disabled')).toBe(true);
|
||||
expect(findTagsList().props('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('shows a status alert', async () => {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ describe('packages_list_row', () => {
|
|||
const findPackagePath = () => wrapper.findComponent(PackagePath);
|
||||
const findDeleteButton = () => wrapper.findByTestId('action-delete');
|
||||
const findInfrastructureIconAndName = () => wrapper.findComponent(InfrastructureIconAndName);
|
||||
const findListItem = () => wrapper.findComponent(ListItem);
|
||||
const findPackageLink = () => wrapper.findComponent(GlLink);
|
||||
const findWarningIcon = () => wrapper.findByTestId('warning-icon');
|
||||
|
||||
|
|
@ -152,10 +151,6 @@ describe('packages_list_row', () => {
|
|||
mountComponent({ packageEntity: { ...packageWithoutTags, status: PACKAGE_ERROR_STATUS } });
|
||||
});
|
||||
|
||||
it('list item has a disabled prop', () => {
|
||||
expect(findListItem().props('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('details link is disabled', () => {
|
||||
expect(findPackageLink().attributes('disabled')).toBeDefined();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -126,19 +126,11 @@ describe('MarkdownDrawer', () => {
|
|||
|
||||
it('triggers renderGLFM in openDrawer', async () => {
|
||||
wrapper.vm.fetchMarkdown();
|
||||
wrapper.vm.openDrawer();
|
||||
wrapper.vm.toggleDrawer();
|
||||
await nextTick();
|
||||
expect(renderGLFMSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers height calculation in openDrawer', async () => {
|
||||
expect(findDrawer().attributes('headerheight')).toBe(`${0}px`);
|
||||
wrapper.vm.fetchMarkdown();
|
||||
wrapper.vm.openDrawer();
|
||||
await nextTick();
|
||||
expect(findDrawer().attributes('headerheight')).toBe(`${100}px`);
|
||||
});
|
||||
|
||||
it('triggers height calculation in toggleDrawer', async () => {
|
||||
expect(findDrawer().attributes('headerheight')).toBe(`${0}px`);
|
||||
wrapper.vm.fetchMarkdown();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import VueApollo from 'vue-apollo';
|
|||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import searchUserProjectsWithIssuesEnabledQuery from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_issues_enabled.query.graphql';
|
||||
import { RESOURCE_TYPES } from '~/vue_shared/components/new_resource_dropdown/constants';
|
||||
import searchProjectsWithinGroupQuery from '~/issues/list/queries/search_projects.query.graphql';
|
||||
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
|
||||
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import {
|
||||
emptySearchProjectsQueryResponse,
|
||||
|
|
@ -25,8 +25,6 @@ import {
|
|||
jest.mock('~/alert');
|
||||
|
||||
describe('NewResourceDropdown component', () => {
|
||||
useLocalStorageSpy();
|
||||
|
||||
let wrapper;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
|
@ -60,6 +58,7 @@ describe('NewResourceDropdown component', () => {
|
|||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findGlDropdownItem = () => wrapper.findComponent(GlDropdownItem);
|
||||
const findInput = () => wrapper.findComponent(GlSearchBoxByType);
|
||||
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
|
||||
const showDropdown = async () => {
|
||||
findDropdown().vm.$emit('shown');
|
||||
await waitForPromises();
|
||||
|
|
@ -183,32 +182,19 @@ describe('NewResourceDropdown component', () => {
|
|||
);
|
||||
});
|
||||
|
||||
describe('without localStorage', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({ mountFn: mount });
|
||||
});
|
||||
|
||||
it('does not attempt to save the selected project to the localStorage', async () => {
|
||||
await showDropdown();
|
||||
findGlDropdownItem().vm.$emit('click', project1);
|
||||
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with localStorage', () => {
|
||||
describe('local storage sync', () => {
|
||||
it('retrieves the selected project from the localStorage', async () => {
|
||||
localStorage.setItem(
|
||||
'group--new-issue-recent-project',
|
||||
JSON.stringify({
|
||||
webUrl: project1.webUrl,
|
||||
name: project1.name,
|
||||
}),
|
||||
);
|
||||
mountComponent({ mountFn: mount, propsData: { withLocalStorage: true } });
|
||||
await nextTick();
|
||||
mountComponent();
|
||||
const dropdown = findDropdown();
|
||||
|
||||
expect(dropdown.props('splitHref')).toBe('');
|
||||
|
||||
findLocalStorageSync().vm.$emit('input', {
|
||||
webUrl: project1.webUrl,
|
||||
name: project1.name,
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
expect(dropdown.props('splitHref')).toBe(
|
||||
joinPaths(project1.webUrl, DASH_SCOPE, 'issues/new'),
|
||||
);
|
||||
|
|
@ -216,17 +202,18 @@ describe('NewResourceDropdown component', () => {
|
|||
});
|
||||
|
||||
it('retrieves legacy cache from the localStorage', async () => {
|
||||
localStorage.setItem(
|
||||
'group--new-issue-recent-project',
|
||||
JSON.stringify({
|
||||
url: `${project1.webUrl}/issues/new`,
|
||||
name: project1.name,
|
||||
}),
|
||||
);
|
||||
mountComponent({ mountFn: mount, propsData: { withLocalStorage: true } });
|
||||
await nextTick();
|
||||
mountComponent();
|
||||
const dropdown = findDropdown();
|
||||
|
||||
expect(dropdown.props('splitHref')).toBe('');
|
||||
|
||||
findLocalStorageSync().vm.$emit('input', {
|
||||
url: `${project1.webUrl}/issues/new`,
|
||||
name: project1.name,
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(dropdown.props('splitHref')).toBe(
|
||||
joinPaths(project1.webUrl, DASH_SCOPE, 'issues/new'),
|
||||
);
|
||||
|
|
@ -234,34 +221,24 @@ describe('NewResourceDropdown component', () => {
|
|||
});
|
||||
|
||||
describe.each(RESOURCE_TYPES)('with resource type %s', (resourceType) => {
|
||||
it('computes the local storage key without a group', async () => {
|
||||
it('computes the local storage key without a group', () => {
|
||||
mountComponent({
|
||||
mountFn: mount,
|
||||
propsData: { resourceType, withLocalStorage: true },
|
||||
propsData: { resourceType },
|
||||
});
|
||||
await showDropdown();
|
||||
findGlDropdownItem().vm.$emit('click', project1);
|
||||
await nextTick();
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenLastCalledWith(
|
||||
expect(findLocalStorageSync().props('storageKey')).toBe(
|
||||
`group--new-${resourceType}-recent-project`,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
|
||||
it('computes the local storage key with a group', async () => {
|
||||
it('computes the local storage key with a group', () => {
|
||||
const groupId = '22';
|
||||
mountComponent({
|
||||
mountFn: mount,
|
||||
propsData: { groupId, resourceType, withLocalStorage: true },
|
||||
propsData: { groupId, resourceType },
|
||||
});
|
||||
await showDropdown();
|
||||
findGlDropdownItem().vm.$emit('click', project1);
|
||||
await nextTick();
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenLastCalledWith(
|
||||
expect(findLocalStorageSync().props('storageKey')).toBe(
|
||||
`group-${groupId}-new-${resourceType}-recent-project`,
|
||||
expect.any(String),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
[
|
||||
{
|
||||
"iid": "1527542",
|
||||
"title": "SyntaxError: Invalid or unexpected token",
|
||||
"createdAt": "2020-04-17T23:18:14.996Z",
|
||||
"assignees": { "nodes": [] }
|
||||
},
|
||||
{
|
||||
"iid": "1527543",
|
||||
"title": "SyntaxError: Invalid or unexpected token by root",
|
||||
"createdAt": "2020-04-17T23:19:14.996Z",
|
||||
"assignees": { "nodes": [] }
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -12,7 +12,6 @@ import {
|
|||
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
||||
import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
|
||||
import PageWrapper from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
|
||||
import mockItems from './mocks/items.json';
|
||||
import mockFilters from './mocks/items_filters.json';
|
||||
|
||||
const EmptyStateSlot = {
|
||||
|
|
@ -64,7 +63,6 @@ describe('AlertManagementEmptyState', () => {
|
|||
projectPath: '/link',
|
||||
},
|
||||
propsData: {
|
||||
items: [],
|
||||
itemsCount: {},
|
||||
pageInfo: {},
|
||||
statusTabs: [],
|
||||
|
|
@ -156,7 +154,7 @@ describe('AlertManagementEmptyState', () => {
|
|||
|
||||
it('renders a table of items if items are present', () => {
|
||||
mountComponent({
|
||||
props: { showItems: true, items: mockItems },
|
||||
props: { showItems: true },
|
||||
});
|
||||
|
||||
expect(ItemsTable().exists()).toBe(true);
|
||||
|
|
@ -182,7 +180,7 @@ describe('AlertManagementEmptyState', () => {
|
|||
describe('Status Filter Tabs', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent({
|
||||
props: { items: mockItems, itemsCount, statusTabs: ITEMS_STATUS_TABS },
|
||||
props: { itemsCount, statusTabs: ITEMS_STATUS_TABS },
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -210,7 +208,6 @@ describe('AlertManagementEmptyState', () => {
|
|||
beforeEach(() => {
|
||||
mountComponent({
|
||||
props: {
|
||||
items: mockItems,
|
||||
itemsCount,
|
||||
statusTabs: ITEMS_STATUS_TABS,
|
||||
pageInfo: { hasNextPage: true },
|
||||
|
|
@ -256,7 +253,6 @@ describe('AlertManagementEmptyState', () => {
|
|||
it('returns nextPage number', async () => {
|
||||
mountComponent({
|
||||
props: {
|
||||
items: mockItems,
|
||||
itemsCount,
|
||||
statusTabs: ITEMS_STATUS_TABS,
|
||||
pageInfo: { hasNextPage: true },
|
||||
|
|
@ -281,7 +277,6 @@ describe('AlertManagementEmptyState', () => {
|
|||
beforeEach(() => {
|
||||
mountComponent({
|
||||
props: {
|
||||
items: mockItems,
|
||||
itemsCount,
|
||||
statusTabs: ITEMS_STATUS_TABS,
|
||||
filterSearchKey: 'items',
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj
|
|||
|
||||
specify { expect(described_class.interfaces).to include(Types::TodoableInterface) }
|
||||
|
||||
specify { expect(described_class.interfaces).to include(::Types::Projects::ProjectInterface) }
|
||||
|
||||
it 'has the expected fields' do
|
||||
expected_fields = %w[
|
||||
user_permissions id full_path path name_with_namespace
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Types::Projects::ProjectBaseField, feature_category: :groups_and_projects do
|
||||
describe 'authorized?' do
|
||||
let(:current_user) { instance_double(User) }
|
||||
let(:object) { instance_double(Project) }
|
||||
let(:ctx) { { current_user: current_user } }
|
||||
|
||||
subject(:field) do
|
||||
described_class.new(name: :name, type: GraphQL::Types::String, null: true)
|
||||
end
|
||||
|
||||
context 'when user has :read_project ability on the object' do
|
||||
it 'returns true' do
|
||||
expect(Ability).to receive(:allowed?).with(current_user, :read_project, object).and_return(true)
|
||||
|
||||
is_expected.to be_authorized(object, nil, ctx)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have :read_project ability on the object' do
|
||||
it 'returns false' do
|
||||
expect(Ability).to receive(:allowed?).with(current_user, :read_project, object).and_return(false)
|
||||
|
||||
is_expected.not_to be_authorized(object, nil, ctx)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Types::Projects::ProjectInterface, feature_category: :groups_and_projects do
|
||||
it 'has the correct name' do
|
||||
expect(described_class.graphql_name).to eq('ProjectInterface')
|
||||
end
|
||||
|
||||
it 'has the expected fields' do
|
||||
expected_fields = %w[id name name_with_namespace description web_url avatar_url]
|
||||
|
||||
expect(described_class.own_fields.size).to eq(expected_fields.size)
|
||||
expect(described_class).to include_graphql_fields(*expected_fields)
|
||||
end
|
||||
|
||||
describe ".resolve_type" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
subject { described_class.resolve_type(project, { current_user: user }) }
|
||||
|
||||
it { is_expected.to eq Types::ProjectType }
|
||||
end
|
||||
end
|
||||
|
|
@ -167,7 +167,8 @@ RSpec.describe Ci::JobToken::AuthorizationsCompactor, feature_category: :secrets
|
|||
target_project: pns2.project)
|
||||
end
|
||||
|
||||
it 'removes them from the compaction process' do
|
||||
it 'removes them from the compaction process',
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/524014' do
|
||||
compactor.compact(6)
|
||||
|
||||
expect(compactor.allowlist_groups).to match_array([ns2])
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ RSpec.describe RuboCop::Cop::UsageData::DistinctCountByLargeForeignKey do
|
|||
let(:msg) { 'Avoid doing `distinct_count` on foreign keys for large tables having above 100 million rows.' }
|
||||
let(:config) do
|
||||
RuboCop::Config.new('UsageData/DistinctCountByLargeForeignKey' => {
|
||||
'AllowedForeignKeys' => allowed_foreign_keys
|
||||
})
|
||||
'AllowedForeignKeys' => allowed_foreign_keys
|
||||
})
|
||||
end
|
||||
|
||||
context 'in an usage data file' do
|
||||
|
|
@ -20,10 +20,10 @@ RSpec.describe RuboCop::Cop::UsageData::DistinctCountByLargeForeignKey do
|
|||
|
||||
context 'when counting by disallowed key' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
distinct_count(Issue, :creator_id)
|
||||
^^^^^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense when batch is false' do
|
||||
|
|
@ -31,10 +31,10 @@ RSpec.describe RuboCop::Cop::UsageData::DistinctCountByLargeForeignKey do
|
|||
end
|
||||
|
||||
it 'registers an offense when batch is true' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
distinct_count(Issue, :creator_id, batch: true)
|
||||
^^^^^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ RSpec.describe RuboCop::Cop::UsageData::HistogramWithLargeTable do
|
|||
|
||||
let(:config) do
|
||||
RuboCop::Config.new('UsageData/HistogramWithLargeTable' => {
|
||||
'HighTrafficModels' => high_traffic_models
|
||||
})
|
||||
'HighTrafficModels' => high_traffic_models
|
||||
})
|
||||
end
|
||||
|
||||
context 'in an usage data file' do
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ RSpec.describe RuboCop::Cop::UsageData::InstrumentationSuperclass do
|
|||
|
||||
let(:config) do
|
||||
RuboCop::Config.new('UsageData/InstrumentationSuperclass' => {
|
||||
'AllowedClasses' => allowed_classes
|
||||
})
|
||||
'AllowedClasses' => allowed_classes
|
||||
})
|
||||
end
|
||||
|
||||
context 'when in an instrumentation file' do
|
||||
|
|
@ -28,10 +28,10 @@ RSpec.describe RuboCop::Cop::UsageData::InstrumentationSuperclass do
|
|||
|
||||
context 'when inheriting from some other superclass' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
class NewMetric < BaseMetric; end
|
||||
^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -51,10 +51,10 @@ RSpec.describe RuboCop::Cop::UsageData::InstrumentationSuperclass do
|
|||
|
||||
context 'when inheriting from some other superclass' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
NewMetric = Class.new(BaseMetric)
|
||||
^^^^^^^^^^ #{msg}
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ RSpec.describe RuboCop::Cop::UsageData::LargeTable do
|
|||
|
||||
let(:config) do
|
||||
RuboCop::Config.new('UsageData/LargeTable' => {
|
||||
'NonRelatedClasses' => large_tables,
|
||||
'CountMethods' => count_methods,
|
||||
'AllowedMethods' => allowed_methods
|
||||
})
|
||||
'NonRelatedClasses' => large_tables,
|
||||
'CountMethods' => count_methods,
|
||||
'AllowedMethods' => allowed_methods
|
||||
})
|
||||
end
|
||||
|
||||
context 'in an usage data file' do
|
||||
|
|
@ -26,19 +26,19 @@ RSpec.describe RuboCop::Cop::UsageData::LargeTable do
|
|||
context 'with large tables' do
|
||||
context 'when calling Issue.count' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
Issue.count
|
||||
^^^^^^^^^^^ #{msg} Issue
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when calling Issue.active.count' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
Issue.active.count
|
||||
^^^^^^^^^^^^ #{msg} Issue
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -56,10 +56,10 @@ RSpec.describe RuboCop::Cop::UsageData::LargeTable do
|
|||
|
||||
context 'when calling Ci::Build.active.count' do
|
||||
it 'registers an offense' do
|
||||
expect_offense(<<~CODE)
|
||||
expect_offense(<<~RUBY)
|
||||
Ci::Build.active.count
|
||||
^^^^^^^^^^^^^^^^ #{msg} Ci::Build
|
||||
CODE
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ RSpec.describe Ci::PipelineArtifacts::CoverageReportService, feature_category: :
|
|||
it 'logs relevant information' do
|
||||
allow(Gitlab::AppLogger).to receive(:info).and_call_original
|
||||
expect(Gitlab::AppLogger).to receive(:info).with({
|
||||
project_id: project.id,
|
||||
pipeline_id: pipeline.id,
|
||||
pipeline_artifact_id: kind_of(Numeric),
|
||||
message: kind_of(String)
|
||||
})
|
||||
project_id: project.id,
|
||||
pipeline_id: pipeline.id,
|
||||
pipeline_artifact_id: kind_of(Numeric),
|
||||
message: kind_of(String)
|
||||
})
|
||||
|
||||
subject
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ RSpec.describe Clusters::UpdateService, feature_category: :deployment_management
|
|||
context 'when service token is empty' do
|
||||
let(:params) do
|
||||
{
|
||||
platform_kubernetes_attributes: {
|
||||
token: ''
|
||||
}
|
||||
platform_kubernetes_attributes: {
|
||||
token: ''
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -69,9 +69,9 @@ RSpec.describe Clusters::UpdateService, feature_category: :deployment_management
|
|||
context 'when service token is not empty' do
|
||||
let(:params) do
|
||||
{
|
||||
platform_kubernetes_attributes: {
|
||||
token: 'new secret token'
|
||||
}
|
||||
platform_kubernetes_attributes: {
|
||||
token: 'new secret token'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ RSpec.describe Notes::RenderService, feature_category: :team_planning do
|
|||
.with(
|
||||
user: user,
|
||||
redaction_context: {
|
||||
requested_path: 'foo',
|
||||
project_wiki: wiki,
|
||||
ref: 'bar',
|
||||
only_path: nil,
|
||||
xhtml: false
|
||||
requested_path: 'foo',
|
||||
project_wiki: wiki,
|
||||
ref: 'bar',
|
||||
only_path: nil,
|
||||
xhtml: false
|
||||
}
|
||||
)
|
||||
.and_call_original
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService, feature_categor
|
|||
shared_examples 'logging an error response' do |message: 'could not delete tags', extra_log: {}|
|
||||
it 'logs an error message' do
|
||||
log_data = {
|
||||
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
||||
message: message,
|
||||
container_repository_id: repository.id,
|
||||
project_id: repository.project_id
|
||||
service_class: 'Projects::ContainerRepository::DeleteTagsService',
|
||||
message: message,
|
||||
container_repository_id: repository.id,
|
||||
project_id: repository.project_id
|
||||
}
|
||||
|
||||
log_data.merge!(extra_log) if extra_log.any?
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ RSpec.describe Projects::CreateFromTemplateService, feature_category: :groups_an
|
|||
let(:template_name) { 'rails' }
|
||||
let(:project_params) do
|
||||
{
|
||||
path: user.to_param,
|
||||
template_name: template_name,
|
||||
description: 'project description',
|
||||
visibility_level: Gitlab::VisibilityLevel::PUBLIC
|
||||
path: user.to_param,
|
||||
template_name: template_name,
|
||||
description: 'project description',
|
||||
visibility_level: Gitlab::VisibilityLevel::PUBLIC
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -402,11 +402,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
_, updates, _ = service.execute(content, issuable)
|
||||
|
||||
expect(updates).to eq(spend_time: {
|
||||
category: nil,
|
||||
duration: 3600,
|
||||
user_id: developer.id,
|
||||
spent_at: DateTime.current
|
||||
})
|
||||
category: nil,
|
||||
duration: 3600,
|
||||
user_id: developer.id,
|
||||
spent_at: DateTime.current
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -417,11 +417,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
_, updates, _ = service.execute(content, issuable)
|
||||
|
||||
expect(updates).to eq(spend_time: {
|
||||
category: nil,
|
||||
duration: -7200,
|
||||
user_id: developer.id,
|
||||
spent_at: DateTime.current
|
||||
})
|
||||
category: nil,
|
||||
duration: -7200,
|
||||
user_id: developer.id,
|
||||
spent_at: DateTime.current
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -437,11 +437,11 @@ RSpec.describe QuickActions::InterpretService, feature_category: :text_editors d
|
|||
_, updates, _ = service.execute(content, issuable)
|
||||
|
||||
expect(updates).to eq(spend_time: {
|
||||
category: nil,
|
||||
duration: 1800,
|
||||
user_id: developer.id,
|
||||
spent_at: Date.parse(date)
|
||||
})
|
||||
category: nil,
|
||||
duration: 1800,
|
||||
user_id: developer.id,
|
||||
spent_at: Date.parse(date)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -318,11 +318,11 @@ RSpec.describe ServicePing::SubmitService, feature_category: :service_ping do
|
|||
uuid: 'uuid',
|
||||
metric_a: metric_double,
|
||||
metric_group: {
|
||||
metric_b: metric_double_with_error
|
||||
},
|
||||
metric_b: metric_double_with_error
|
||||
},
|
||||
metric_without_timing: "value",
|
||||
recorded_at: Time.current
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:metadata_payload) do
|
||||
|
|
@ -333,8 +333,8 @@ RSpec.describe ServicePing::SubmitService, feature_category: :service_ping do
|
|||
{ name: 'metric_a', time_elapsed: 123 },
|
||||
{ name: 'metric_group.metric_b', time_elapsed: 123, error: 'Error' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it 'submits metadata' do
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/SpecFilePathFor
|
|||
|
||||
expect(Bundler).to receive(:with_original_env).and_call_original.twice
|
||||
|
||||
expect(Process).to receive(:spawn).ordered.with({
|
||||
expect(Process).to receive(:spawn).ordered.with(
|
||||
{
|
||||
"ENABLE_SIDEKIQ_CLUSTER" => "1",
|
||||
"SIDEKIQ_WORKER_ID" => "0"
|
||||
},
|
||||
|
|
@ -23,9 +24,10 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/SpecFilePathFor
|
|||
).and_return(1)
|
||||
expect(Process).to receive(:detach).ordered.with(1)
|
||||
|
||||
expect(Process).to receive(:spawn).ordered.with({
|
||||
"ENABLE_SIDEKIQ_CLUSTER" => "1",
|
||||
"SIDEKIQ_WORKER_ID" => "1"
|
||||
expect(Process).to receive(:spawn).ordered.with(
|
||||
{
|
||||
"ENABLE_SIDEKIQ_CLUSTER" => "1",
|
||||
"SIDEKIQ_WORKER_ID" => "1"
|
||||
},
|
||||
"bundle", "exec", "sidekiq", "-c20", "-eproduction", "-t25", "-gqueues:bar,baz", "-rfoo/bar", "-qbar,1", "-qbaz,1", process_options
|
||||
).and_return(2)
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ RSpec.describe Spam::Concerns::HasSpamActionResponseFields do
|
|||
it 'merges in spam action fields from spammable' do
|
||||
expect(subject.spam_action_response_fields(spammable))
|
||||
.to eq({
|
||||
spam: true,
|
||||
needs_captcha_response: true,
|
||||
spam_log_id: 1,
|
||||
captcha_site_key: recaptcha_site_key
|
||||
})
|
||||
spam: true,
|
||||
needs_captcha_response: true,
|
||||
spam_log_id: 1,
|
||||
captcha_site_key: recaptcha_site_key
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@ module KubernetesHelpers
|
|||
|
||||
def stub_server_min_version(min_version)
|
||||
response = kube_response({
|
||||
major: "1", # not used, just added here to be a bit more realistic purposes
|
||||
minor: min_version.to_s
|
||||
})
|
||||
major: "1", # not used, just added here to be a bit more realistic purposes
|
||||
minor: min_version.to_s
|
||||
})
|
||||
|
||||
WebMock.stub_request(:get, service.api_url + '/version')
|
||||
.with(
|
||||
|
|
@ -759,12 +759,12 @@ module KubernetesHelpers
|
|||
},
|
||||
"spec" => {
|
||||
"containers" => [{
|
||||
"env" =>
|
||||
[{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }],
|
||||
"image" => "image_name",
|
||||
"name" => "user-container",
|
||||
"resources" => {}
|
||||
}],
|
||||
"env" =>
|
||||
[{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }],
|
||||
"image" => "image_name",
|
||||
"name" => "user-container",
|
||||
"resources" => {}
|
||||
}],
|
||||
"timeoutSeconds" => 300
|
||||
}
|
||||
},
|
||||
|
|
@ -810,12 +810,12 @@ module KubernetesHelpers
|
|||
},
|
||||
"spec" => {
|
||||
"containers" => [{
|
||||
"env" =>
|
||||
[{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }],
|
||||
"image" => "image_name",
|
||||
"name" => "user-container",
|
||||
"resources" => {}
|
||||
}],
|
||||
"env" =>
|
||||
[{ "name" => "timestamp", "value" => "2019-10-22 21:19:12" }],
|
||||
"image" => "image_name",
|
||||
"name" => "user-container",
|
||||
"resources" => {}
|
||||
}],
|
||||
"timeoutSeconds" => 300
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module WikiHelpers
|
|||
params = {
|
||||
file_name: file_name,
|
||||
file_content: File.read(expand_fixture_path(file_name))
|
||||
}
|
||||
}
|
||||
|
||||
::Wikis::CreateAttachmentService.new(
|
||||
container: wiki.container,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ RSpec.shared_context 'container registry client' do
|
|||
let(:client) { described_class.new(registry_api_url, options) }
|
||||
let(:push_blob_headers) do
|
||||
{
|
||||
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
|
||||
'Authorization' => "bearer #{token}",
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
|
||||
'Accept' => 'application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json',
|
||||
'Authorization' => "bearer #{token}",
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'User-Agent' => "GitLab/#{Gitlab::VERSION}"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ RSpec.shared_context 'ProjectPolicy context' do
|
|||
award_emoji create_issue create_note
|
||||
create_project read_issue_board read_issue read_issue_iid read_issue_link
|
||||
read_label read_issue_board_list read_milestone read_note read_project
|
||||
read_project_for_iids read_project_member read_release read_snippet
|
||||
read_project_for_iids read_project_member read_project_metadata read_release read_snippet
|
||||
read_wiki upload_file
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -71,13 +71,13 @@ RSpec.shared_examples 'a harbor artifacts controller' do |args|
|
|||
|
||||
before do
|
||||
stub_request(:get,
|
||||
"https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts"\
|
||||
"?page=1&page_size=10&with_tag=true")
|
||||
.with(
|
||||
headers: {
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
|
||||
"https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts" \
|
||||
"?page=1&page_size=10&with_tag=true")
|
||||
.with(
|
||||
headers: {
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
|
||||
container.add_reporter(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ RSpec.shared_examples 'a harbor repositories controller' do |args|
|
|||
stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories?page=1&page_size=10")
|
||||
.with(
|
||||
headers: {
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_repositories.to_json, headers: { "x-total-count": 2 })
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_repositories.to_json, headers: { "x-total-count": 2 })
|
||||
container.add_reporter(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ RSpec.shared_examples 'a harbor tags controller' do |args|
|
|||
|
||||
before do
|
||||
stub_request(:get,
|
||||
"https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags"\
|
||||
"?page=1&page_size=10")
|
||||
.with(
|
||||
headers: {
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
|
||||
"https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags" \
|
||||
"?page=1&page_size=10")
|
||||
.with(
|
||||
headers: {
|
||||
Authorization: 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
|
||||
'Content-Type': 'application/json'
|
||||
}).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
|
||||
container.add_reporter(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ RSpec.shared_examples '#prometheus_client shared' do
|
|||
expect(Gitlab::PrometheusClient)
|
||||
.to(receive(:new))
|
||||
.with(a_valid_url, kube_client.rest_client.options.merge({
|
||||
headers: kube_client.headers,
|
||||
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
|
||||
}))
|
||||
headers: kube_client.headers,
|
||||
timeout: PrometheusAdapter::DEFAULT_PROMETHEUS_REQUEST_TIMEOUT_SEC
|
||||
}))
|
||||
subject.prometheus_client
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -270,9 +270,9 @@ RSpec.shared_examples 'handling audit request' do |path:, scope: :project|
|
|||
let(:params) do
|
||||
ActiveSupport::Gzip.compress(
|
||||
Gitlab::Json.dump({
|
||||
'@gitlab-org/npm-test': ['1.0.6'],
|
||||
'call-bind': ['1.0.2']
|
||||
})
|
||||
'@gitlab-org/npm-test': ['1.0.6'],
|
||||
'call-bind': ['1.0.2']
|
||||
})
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,9 @@ RSpec.describe CI::ChangedFiles, feature_category: :tooling do
|
|||
|
||||
describe '#run_eslint_for_changed_files' do
|
||||
let(:files) { ['file1.js', 'file2.vue'] }
|
||||
let(:eslint_command) { ['yarn', 'run', 'lint:eslint', '--format', 'gitlab', 'file1.js', 'file2.vue'] }
|
||||
let(:eslint_command) do
|
||||
['yarn', 'run', 'lint:eslint', '--no-warn-ignored', '--format', 'gitlab', 'file1.js', 'file2.vue']
|
||||
end
|
||||
|
||||
before do
|
||||
allow(instance).to receive(:puts).with('Running ESLint...')
|
||||
|
|
|
|||
|
|
@ -133,10 +133,10 @@ RSpec.describe 'layouts/_head' do
|
|||
|
||||
before do
|
||||
stub_config(extra: {
|
||||
matomo_url: matomo_host,
|
||||
matomo_site_id: 12345,
|
||||
matomo_disable_cookies: false
|
||||
})
|
||||
matomo_url: matomo_host,
|
||||
matomo_site_id: 12345,
|
||||
matomo_disable_cookies: false
|
||||
})
|
||||
end
|
||||
|
||||
it 'add a Matomo Javascript' do
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ module CI
|
|||
|
||||
return 0 if files.empty?
|
||||
|
||||
command = ["yarn", "run", "lint:eslint", "--format", "gitlab", *files]
|
||||
command = ["yarn", "run", "lint:eslint", "--no-warn-ignored", "--format", "gitlab", *files]
|
||||
system(*command)
|
||||
|
||||
last_command_status.exitstatus
|
||||
|
|
|
|||
Loading…
Reference in New Issue