Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9b863f753f
commit
c7eec01f1b
|
|
@ -1 +1 @@
|
|||
dbf0cf7484ba1265ee1cd5e3a49e04da933e931c
|
||||
d15b9c84faee3eb178e7c7d9360832f26d4107a2
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ import {
|
|||
|
||||
export default {
|
||||
name: 'DeleteModal',
|
||||
i18n: {
|
||||
DELETE_MODAL_CONTENT,
|
||||
DELETE_PACKAGES_MODAL_DESCRIPTION,
|
||||
},
|
||||
components: {
|
||||
GlLink,
|
||||
GlModal,
|
||||
|
|
@ -38,15 +34,21 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
itemToBeDeleted() {
|
||||
if (this.itemsToBeDeleted.length === 1) {
|
||||
const [itemToBeDeleted] = this.itemsToBeDeleted;
|
||||
return itemToBeDeleted;
|
||||
}
|
||||
return null;
|
||||
return this.itemsToBeDeleted.length === 1 ? this.itemsToBeDeleted[0] : null;
|
||||
},
|
||||
title() {
|
||||
return this.itemToBeDeleted ? DELETE_MODAL_TITLE : DELETE_PACKAGES_MODAL_TITLE;
|
||||
},
|
||||
packageDescription() {
|
||||
return this.showRequestForwardingContent
|
||||
? DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT
|
||||
: DELETE_MODAL_CONTENT;
|
||||
},
|
||||
packagesDescription() {
|
||||
return this.showRequestForwardingContent
|
||||
? DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT
|
||||
: DELETE_PACKAGES_MODAL_DESCRIPTION;
|
||||
},
|
||||
packagesDeletePrimaryActionProps() {
|
||||
let text = DELETE_PACKAGE_MODAL_PRIMARY_ACTION;
|
||||
|
||||
|
|
@ -62,11 +64,6 @@ export default {
|
|||
attributes: { variant: 'danger', category: 'primary' },
|
||||
};
|
||||
},
|
||||
requestForwardingContentMessage() {
|
||||
return this.itemToBeDeleted
|
||||
? DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT
|
||||
: DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT;
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
cancelAction: {
|
||||
|
|
@ -95,24 +92,23 @@ export default {
|
|||
@primary="$emit('confirm')"
|
||||
@cancel="$emit('cancel')"
|
||||
>
|
||||
<p v-if="showRequestForwardingContent">
|
||||
<gl-sprintf :message="requestForwardingContentMessage">
|
||||
<template #docLink="{ content }">
|
||||
<p>
|
||||
<gl-sprintf v-if="itemToBeDeleted" :message="packageDescription">
|
||||
<template v-if="showRequestForwardingContent" #docLink="{ content }">
|
||||
<gl-link :href="$options.links.REQUEST_FORWARDING_HELP_PAGE_PATH">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p v-else>
|
||||
<gl-sprintf v-if="itemToBeDeleted" :message="$options.i18n.DELETE_MODAL_CONTENT">
|
||||
<template #version>
|
||||
<strong>{{ itemToBeDeleted.version }}</strong>
|
||||
</template>
|
||||
|
||||
<template #name>
|
||||
<strong>{{ itemToBeDeleted.name }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<gl-sprintf v-else :message="$options.i18n.DELETE_PACKAGES_MODAL_DESCRIPTION">
|
||||
<gl-sprintf v-else :message="packagesDescription">
|
||||
<template v-if="showRequestForwardingContent" #docLink="{ content }">
|
||||
<gl-link :href="$options.links.REQUEST_FORWARDING_HELP_PAGE_PATH">{{ content }}</gl-link>
|
||||
</template>
|
||||
|
||||
<template #count>
|
||||
{{ itemsToBeDeleted.length }}
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import VersionRow from '~/packages_and_registries/package_registry/components/de
|
|||
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
|
||||
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue';
|
||||
import {
|
||||
CANCEL_DELETE_PACKAGE_VERSION_TRACKING_ACTION,
|
||||
CANCEL_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION,
|
||||
|
|
@ -24,7 +23,6 @@ import getPackageVersionsQuery from '~/packages_and_registries/package_registry/
|
|||
export default {
|
||||
components: {
|
||||
DeleteModal,
|
||||
DeletePackageModal,
|
||||
GlAlert,
|
||||
VersionRow,
|
||||
PackagesListLoader,
|
||||
|
|
@ -47,6 +45,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
isRequestForwardingEnabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
packageId: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
|
@ -54,7 +57,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
itemToBeDeleted: null,
|
||||
itemsToBeDeleted: [],
|
||||
packageVersions: {},
|
||||
fetchPackageVersionsError: false,
|
||||
|
|
@ -79,6 +81,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
itemToBeDeleted() {
|
||||
return this.itemsToBeDeleted.length === 1 ? this.itemsToBeDeleted[0] : null;
|
||||
},
|
||||
isListEmpty() {
|
||||
return this.count === 0;
|
||||
},
|
||||
|
|
@ -110,36 +115,31 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
deleteItemConfirmation() {
|
||||
this.$emit('delete', [this.itemToBeDeleted]);
|
||||
this.track(DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
this.itemToBeDeleted = null;
|
||||
},
|
||||
deleteItemCanceled() {
|
||||
this.track(CANCEL_DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
this.itemToBeDeleted = null;
|
||||
},
|
||||
deleteItemsCanceled() {
|
||||
this.track(CANCEL_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
if (this.itemToBeDeleted) {
|
||||
this.track(CANCEL_DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
} else {
|
||||
this.track(CANCEL_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
}
|
||||
|
||||
this.itemsToBeDeleted = [];
|
||||
},
|
||||
deleteItemsConfirmation() {
|
||||
this.$emit('delete', this.itemsToBeDeleted);
|
||||
this.track(DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
if (this.itemToBeDeleted) {
|
||||
this.track(DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
} else {
|
||||
this.track(DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
}
|
||||
this.itemsToBeDeleted = [];
|
||||
},
|
||||
setItemToBeDeleted(item) {
|
||||
this.itemToBeDeleted = { ...item };
|
||||
this.track(REQUEST_DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
},
|
||||
setItemsToBeDeleted(items) {
|
||||
if (items.length === 1) {
|
||||
const [item] = items;
|
||||
this.setItemToBeDeleted(item);
|
||||
return;
|
||||
}
|
||||
this.itemsToBeDeleted = items;
|
||||
this.track(REQUEST_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
if (items.length === 1) {
|
||||
this.track(REQUEST_DELETE_PACKAGE_VERSION_TRACKING_ACTION);
|
||||
} else {
|
||||
this.track(REQUEST_DELETE_PACKAGE_VERSIONS_TRACKING_ACTION);
|
||||
}
|
||||
this.$refs.deletePackagesModal.show();
|
||||
},
|
||||
fetchPreviousVersionsPage() {
|
||||
|
|
@ -196,21 +196,16 @@ export default {
|
|||
:first="first"
|
||||
:package-entity="item"
|
||||
:selected="isSelected(item)"
|
||||
@delete="setItemToBeDeleted(item)"
|
||||
@delete="setItemsToBeDeleted([item])"
|
||||
@select="selectItem(item)"
|
||||
/>
|
||||
</template>
|
||||
</registry-list>
|
||||
|
||||
<delete-package-modal
|
||||
:item-to-be-deleted="itemToBeDeleted"
|
||||
@ok="deleteItemConfirmation"
|
||||
@cancel="deleteItemCanceled"
|
||||
/>
|
||||
|
||||
<delete-modal
|
||||
ref="deletePackagesModal"
|
||||
:items-to-be-deleted="itemsToBeDeleted"
|
||||
:show-request-forwarding-content="isRequestForwardingEnabled"
|
||||
@confirm="deleteItemsConfirmation"
|
||||
@cancel="deleteItemsCanceled"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export const DELETE_PACKAGES_WITH_REQUEST_FORWARDING_PRIMARY_ACTION = s__(
|
|||
'PackageRegistry|Yes, delete selected packages',
|
||||
);
|
||||
export const DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT = s__(
|
||||
'PackageRegistry|Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete the package anyway? %{docLinkStart}What are the risks?%{docLinkEnd}',
|
||||
'PackageRegistry|Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete %{name} version %{version} anyway? %{docLinkStart}What are the risks?%{docLinkEnd}',
|
||||
);
|
||||
export const DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT = s__(
|
||||
'PackageRegistry|Some of the selected package formats allow request forwarding. Deleting a package while request forwarding is enabled for the project can pose a security risk. Do you want to proceed with deleting the selected packages? %{docLinkStart}What are the risks?%{docLinkEnd}',
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
#import "~/packages_and_registries/package_registry/graphql/fragments/package_group_settings.fragment.graphql"
|
||||
|
||||
query getPackageDetails($id: PackagesPackageID!) {
|
||||
package(id: $id) {
|
||||
id
|
||||
|
|
@ -23,6 +25,9 @@ query getPackageDetails($id: PackagesPackageID!) {
|
|||
path
|
||||
name
|
||||
fullPath
|
||||
group {
|
||||
...GroupPackageSettings
|
||||
}
|
||||
}
|
||||
tags(first: 10) {
|
||||
nodes {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import {
|
||||
GlBadge,
|
||||
GlButton,
|
||||
GlLink,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlTooltipDirective,
|
||||
|
|
@ -37,6 +38,7 @@ import {
|
|||
DELETE_PACKAGE_FILE_TRACKING_ACTION,
|
||||
DELETE_PACKAGE_FILES_TRACKING_ACTION,
|
||||
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION,
|
||||
REQUEST_FORWARDING_HELP_PAGE_PATH,
|
||||
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
|
||||
SHOW_DELETE_SUCCESS_ALERT,
|
||||
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
|
||||
|
|
@ -44,6 +46,7 @@ import {
|
|||
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
|
||||
DELETE_PACKAGE_FILES_ERROR_MESSAGE,
|
||||
DELETE_PACKAGE_FILES_SUCCESS_MESSAGE,
|
||||
DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT,
|
||||
DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION,
|
||||
DELETE_MODAL_TITLE,
|
||||
DELETE_MODAL_CONTENT,
|
||||
|
|
@ -64,6 +67,7 @@ export default {
|
|||
GlButton,
|
||||
GlEmptyState,
|
||||
GlModal,
|
||||
GlLink,
|
||||
GlTab,
|
||||
GlTabs,
|
||||
GlSprintf,
|
||||
|
|
@ -124,6 +128,11 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
deleteModalContent() {
|
||||
return this.isRequestForwardingEnabled
|
||||
? DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT
|
||||
: this.deletePackageModalContent;
|
||||
},
|
||||
projectName() {
|
||||
return this.packageEntity.project?.name;
|
||||
},
|
||||
|
|
@ -170,6 +179,12 @@ export default {
|
|||
showFiles() {
|
||||
return this.packageType !== PACKAGE_TYPE_COMPOSER;
|
||||
},
|
||||
groupSettings() {
|
||||
return this.packageEntity.project?.group?.packageSettings ?? {};
|
||||
},
|
||||
isRequestForwardingEnabled() {
|
||||
return this.groupSettings[`${this.packageType.toLowerCase()}PackageRequestsForwarding`];
|
||||
},
|
||||
showMetadata() {
|
||||
return [
|
||||
PACKAGE_TYPE_COMPOSER,
|
||||
|
|
@ -291,6 +306,9 @@ export default {
|
|||
),
|
||||
otherVersionsTabTitle: s__('PackageRegistry|Other versions'),
|
||||
},
|
||||
links: {
|
||||
REQUEST_FORWARDING_HELP_PAGE_PATH,
|
||||
},
|
||||
modal: {
|
||||
packageDeletePrimaryAction: {
|
||||
text: s__('PackageRegistry|Permanently delete'),
|
||||
|
|
@ -398,6 +416,7 @@ export default {
|
|||
:can-destroy="packageEntity.canDestroy"
|
||||
:count="packageVersionsCount"
|
||||
:is-mutation-loading="versionsMutationLoading"
|
||||
:is-request-forwarding-enabled="isRequestForwardingEnabled"
|
||||
:package-id="packageEntity.id"
|
||||
@delete="deletePackages"
|
||||
>
|
||||
|
|
@ -429,15 +448,23 @@ export default {
|
|||
@canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE)"
|
||||
>
|
||||
<template #modal-title>{{ $options.i18n.DELETE_MODAL_TITLE }}</template>
|
||||
<gl-sprintf :message="deletePackageModalContent">
|
||||
<template #version>
|
||||
<strong>{{ packageEntity.version }}</strong>
|
||||
</template>
|
||||
<p>
|
||||
<gl-sprintf :message="deleteModalContent">
|
||||
<template v-if="isRequestForwardingEnabled" #docLink="{ content }">
|
||||
<gl-link :href="$options.links.REQUEST_FORWARDING_HELP_PAGE_PATH">{{
|
||||
content
|
||||
}}</gl-link>
|
||||
</template>
|
||||
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<template #version>
|
||||
<strong>{{ packageEntity.version }}</strong>
|
||||
</template>
|
||||
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</gl-modal>
|
||||
</template>
|
||||
</delete-packages>
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ const initForkInfo = () => {
|
|||
canSyncBranch,
|
||||
aheadComparePath,
|
||||
behindComparePath,
|
||||
canUserCreateMrInFork,
|
||||
createMrPath,
|
||||
viewMrPath,
|
||||
} = forkEl.dataset;
|
||||
return new Vue({
|
||||
el: forkEl,
|
||||
|
|
@ -132,8 +132,8 @@ const initForkInfo = () => {
|
|||
sourceDefaultBranch,
|
||||
aheadComparePath,
|
||||
behindComparePath,
|
||||
canUserCreateMrInFork,
|
||||
createMrPath,
|
||||
viewMrPath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export const i18n = {
|
|||
error: s__('ForksDivergence|Failed to fetch fork details. Try again later.'),
|
||||
updateFork: s__('ForksDivergence|Update fork'),
|
||||
createMergeRequest: s__('ForksDivergence|Create merge request'),
|
||||
viewMergeRequest: s__('ForksDivergence|View merge request'),
|
||||
successMessage: s__(
|
||||
'ForksDivergence|Successfully fetched and merged from the upstream repository.',
|
||||
),
|
||||
|
|
@ -121,10 +122,10 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
canUserCreateMrInFork: {
|
||||
type: Boolean,
|
||||
viewMrPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
@ -203,7 +204,10 @@ export default {
|
|||
);
|
||||
},
|
||||
hasCreateMrButton() {
|
||||
return this.canUserCreateMrInFork && this.ahead && this.createMrPath;
|
||||
return this.ahead && this.createMrPath;
|
||||
},
|
||||
hasViewMrButton() {
|
||||
return this.viewMrPath;
|
||||
},
|
||||
forkDivergenceMessage() {
|
||||
if (!this.forkDetails) {
|
||||
|
|
@ -319,6 +323,14 @@ export default {
|
|||
>
|
||||
<span>{{ $options.i18n.createMergeRequest }}</span>
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="hasViewMrButton"
|
||||
class="gl-ml-4"
|
||||
:href="viewMrPath"
|
||||
data-testid="view-mr-button"
|
||||
>
|
||||
<span>{{ $options.i18n.viewMergeRequest }}</span>
|
||||
</gl-button>
|
||||
<gl-button
|
||||
v-if="hasUpdateButton"
|
||||
class="gl-ml-4"
|
||||
|
|
|
|||
|
|
@ -84,10 +84,10 @@ export default function setupVueRepositoryList() {
|
|||
sourcePath,
|
||||
sourceDefaultBranch,
|
||||
createMrPath,
|
||||
viewMrPath,
|
||||
canSyncBranch,
|
||||
aheadComparePath,
|
||||
behindComparePath,
|
||||
canUserCreateMrInFork,
|
||||
} = forkEl.dataset;
|
||||
return new Vue({
|
||||
el: forkEl,
|
||||
|
|
@ -104,7 +104,7 @@ export default function setupVueRepositoryList() {
|
|||
aheadComparePath,
|
||||
behindComparePath,
|
||||
createMrPath,
|
||||
canUserCreateMrInFork,
|
||||
viewMrPath,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
module ProjectsHelper
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include CompareHelper
|
||||
include Gitlab::Allowable
|
||||
|
||||
def project_incident_management_setting
|
||||
@project_incident_management_setting ||= @project.incident_management_setting ||
|
||||
|
|
@ -131,6 +132,10 @@ module ProjectsHelper
|
|||
|
||||
source_default_branch = source_project.default_branch
|
||||
|
||||
merge_request =
|
||||
MergeRequest.opened
|
||||
.from_project(project).of_projects(source_project.id).from_source_branches(ref).first
|
||||
|
||||
{
|
||||
project_path: project.full_path,
|
||||
selected_branch: ref,
|
||||
|
|
@ -141,11 +146,11 @@ module ProjectsHelper
|
|||
ahead_compare_path: project_compare_path(
|
||||
project, from: source_default_branch, to: ref, from_project_id: source_project.id
|
||||
),
|
||||
create_mr_path: create_mr_path(from: ref, source_project: project, to: source_default_branch, target_project: source_project),
|
||||
create_mr_path: create_merge_request_path(project, source_project, ref, merge_request),
|
||||
view_mr_path: merge_request && project_merge_request_path(source_project, merge_request),
|
||||
behind_compare_path: project_compare_path(
|
||||
source_project, from: ref, to: source_default_branch, from_project_id: project.id
|
||||
),
|
||||
can_user_create_mr_in_fork: can_user_create_mr_in_fork(source_project)
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -167,10 +172,6 @@ module ProjectsHelper
|
|||
project.fork_source if project.fork_source && can?(current_user, :read_project, project.fork_source)
|
||||
end
|
||||
|
||||
def can_user_create_mr_in_fork(project)
|
||||
can?(current_user, :create_merge_request_in, project)
|
||||
end
|
||||
|
||||
def project_search_tabs?(tab)
|
||||
return false unless @project.present?
|
||||
|
||||
|
|
@ -523,6 +524,18 @@ module ProjectsHelper
|
|||
|
||||
private
|
||||
|
||||
def create_merge_request_path(project, source_project, ref, merge_request)
|
||||
return if merge_request.present?
|
||||
return unless can?(current_user, :create_merge_request_from, project)
|
||||
return unless can?(current_user, :create_merge_request_in, source_project)
|
||||
|
||||
create_mr_path(
|
||||
from: ref,
|
||||
source_project: project,
|
||||
to: source_project.default_branch,
|
||||
target_project: source_project)
|
||||
end
|
||||
|
||||
def localized_access_names
|
||||
{
|
||||
Gitlab::Access::NO_ACCESS => _('No access'),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ module Ci
|
|||
include Presentable
|
||||
include ChronicDurationAttribute
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include IgnorableColumns
|
||||
include SafelyChangeColumnDefault
|
||||
|
||||
self.table_name = 'p_ci_builds_metadata'
|
||||
|
|
@ -19,8 +18,6 @@ module Ci
|
|||
|
||||
partitionable scope: :build
|
||||
|
||||
ignore_column :runner_machine_id, remove_with: '16.0', remove_after: '2023-04-22'
|
||||
|
||||
belongs_to :build, class_name: 'CommitStatus'
|
||||
belongs_to :project
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,20 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
|
|||
condition(:request_access_enabled) { @subject.request_access_enabled }
|
||||
|
||||
condition(:create_projects_disabled, scope: :subject) do
|
||||
@subject.project_creation_level == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS
|
||||
next true if @user.nil?
|
||||
|
||||
visibility_levels = if @user.can_admin_all_resources?
|
||||
# admin can create projects even with restricted visibility levels
|
||||
Gitlab::VisibilityLevel.values
|
||||
else
|
||||
Gitlab::VisibilityLevel.allowed_levels
|
||||
end
|
||||
|
||||
allowed_visibility_levels = visibility_levels.select do |level|
|
||||
Project.new(namespace: @subject).visibility_level_allowed?(level)
|
||||
end
|
||||
|
||||
@subject.project_creation_level == ::Gitlab::Access::NO_ONE_PROJECT_ACCESS || allowed_visibility_levels.empty?
|
||||
end
|
||||
|
||||
condition(:developer_maintainer_access, scope: :subject) do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable BackgroundMigration/MissingDictionaryFile
|
||||
|
||||
class RescheduleMigrationForLinks < Gitlab::Database::Migration[2.1]
|
||||
MIGRATION = 'MigrateLinksForVulnerabilityFindings'
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
|
|
@ -13,20 +11,10 @@ class RescheduleMigrationForLinks < Gitlab::Database::Migration[2.1]
|
|||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:vulnerability_occurrences,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
# no-op as it is rescheduled via db/post_migrate/20230412141541_reschedule_links_avoiding_duplication.rb
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||
# no-op as it is rescheduled via db/post_migrate/20230412141541_reschedule_links_avoiding_duplication.rb
|
||||
end
|
||||
end
|
||||
# rubocop: enable BackgroundMigration/MissingDictionaryFile
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable BackgroundMigration/MissingDictionaryFile
|
||||
|
||||
class RescheduleLinksAvoidingDuplication < Gitlab::Database::Migration[2.1]
|
||||
MIGRATION = 'MigrateLinksForVulnerabilityFindings'
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
SUB_BATCH_SIZE = 500
|
||||
BATCH_SIZE = 10000
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:vulnerability_occurrences,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, [])
|
||||
end
|
||||
end
|
||||
# rubocop: enable BackgroundMigration/MissingDictionaryFile
|
||||
|
|
@ -1,15 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNamespacesByTopLevelNamespaceIndex < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_on_namespaces_namespaces_by_top_level_namespace'
|
||||
|
||||
def up
|
||||
prepare_async_index :namespaces, '((traversal_ids[1]), type, id)', name: INDEX_NAME
|
||||
# no-op: re-implemented in AddNamespacesByTopLevelNamespaceIndexV2
|
||||
end
|
||||
|
||||
def down
|
||||
unprepare_async_index_by_name :ci_builds, INDEX_NAME
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNamespacesByTopLevelNamespaceIndexV2 < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_on_namespaces_namespaces_by_top_level_namespace'
|
||||
|
||||
def up
|
||||
unprepare_async_index_by_name :namespaces, INDEX_NAME
|
||||
prepare_async_index :namespaces, '(traversal_ids[1]), type, id', name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
unprepare_async_index_by_name :namespaces, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
777065413e8eb5605037885fb1a38c74b1f464733afb2718380f081edb9ab8a8
|
||||
|
|
@ -0,0 +1 @@
|
|||
101dac198ed6204b5b74f809765d2a7f1907907fdfcfbe579989b8fcf61610c0
|
||||
|
|
@ -8,12 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
Use the Import API to import repositories from GitHub or Bitbucket Server.
|
||||
|
||||
Related APIs include:
|
||||
|
||||
- [Group migration by direct transfer API](bulk_imports.md).
|
||||
- [Group import and export API](group_import_export.md).
|
||||
- [Project import and export API](project_import_export.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
For information on prerequisites for using the Import API, see:
|
||||
|
|
@ -214,3 +208,9 @@ curl --request POST \
|
|||
|
||||
For information on automating user, group, and project import API calls, see
|
||||
[Automate group and project import](../user/project/import/index.md#automate-group-and-project-import).
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Group migration by direct transfer API](bulk_imports.md).
|
||||
- [Group import and export API](group_import_export.md).
|
||||
- [Project import and export API](project_import_export.md).
|
||||
|
|
|
|||
|
|
@ -57,6 +57,59 @@ get do
|
|||
end
|
||||
```
|
||||
|
||||
## Breaking changes
|
||||
|
||||
We must not make breaking changes to our REST API v4, even in major GitLab releases.
|
||||
|
||||
Our REST API maintains its own versioning independent of GitLab versioning.
|
||||
The current REST API version is `4`. [We commit to follow semantic versioning for our REST API](../api/rest/index.md#compatibility-guidelines),
|
||||
which means we cannot make breaking changes until a major version change (most likely, `5`).
|
||||
|
||||
Because version `5` is not scheduled, we allow rare [exceptions](#exceptions).
|
||||
|
||||
### Accommodating backward compatibility instead of breaking changes
|
||||
|
||||
Backward compatibility can often be accommodated in the API by continuing to adapt a changed feature to
|
||||
the old API schema. For example, our REST API
|
||||
[exposes](https://gitlab.com/gitlab-org/gitlab/-/blob/c104f6b8/lib/api/entities/merge_request_basic.rb#L43-47) both
|
||||
`work_in_progress` and `draft` fields.
|
||||
|
||||
### Exceptions
|
||||
|
||||
The exception is only when:
|
||||
|
||||
- A feature must be removed in a major GitLab release.
|
||||
- Backward compatibility cannot be maintained
|
||||
[in any form](#accommodating-backward-compatibility-instead-of-breaking-changes).
|
||||
|
||||
This exception should be rare.
|
||||
|
||||
Even in this exception, rather than removing a field or argument, we must always do the following:
|
||||
|
||||
- Return an empty response for a field (for example, `"null"` or `[]`).
|
||||
- Turn an argument into a no-op.
|
||||
|
||||
## What is a breaking change
|
||||
|
||||
Some examples of breaking changes are:
|
||||
|
||||
- Removing or renaming fields, arguments, or enum values.
|
||||
- Removing endpoints.
|
||||
- Adding new redirects (not all clients follow redirects).
|
||||
- Changing the type of fields in the response (for example, from `String` to `Integer`).
|
||||
- Adding a new **required** argument.
|
||||
- Changing authentication, authorization, or other header requirements.
|
||||
- Changing [any status code](../api/rest/index.md#status-codes) other than `500`.
|
||||
|
||||
## What is not a breaking change
|
||||
|
||||
Some examples of non-breaking changes:
|
||||
|
||||
- Any additive change, such as adding endpoints, non-required arguments, fields, or enum values.
|
||||
- Changes to error messages.
|
||||
- Changes from a `500` status code to [any supported status code](../api/rest/index.md#status-codes) (this is a bugfix).
|
||||
- Changes to the order of fields returned in a response.
|
||||
|
||||
## Declared parameters
|
||||
|
||||
Grape allows you to access only the parameters that have been declared by your
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ The following steps describe setting up an environment to test the GitLab OAuth
|
|||
- Confidential: **No**
|
||||
1. Copy the Application ID.
|
||||
1. Go to **Admin > Settings > General**.
|
||||
1. Scroll down and expand the GitLab for Jira App section.
|
||||
1. Expand **GitLab for Jira App**.
|
||||
1. Go to [gitpod.io/variables](https://gitpod.io/variables).
|
||||
1. Paste the Application ID into the **Jira Connect Application ID** field and select **Save changes**.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ With the [GitLab for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/
|
|||
you can integrate GitLab and Jira Cloud.
|
||||
|
||||
If you use GitLab.com and Jira Cloud, you can install the GitLab for Jira Cloud app.
|
||||
If you do not use both of these environments, use the [Jira DVCS Connector](dvcs/index.md) instead
|
||||
If you do not use both of these environments, use the [Jira DVCS connector](dvcs/index.md) instead
|
||||
or [install the GitLab for Jira Cloud app manually](#install-the-gitlab-for-jira-cloud-app-manually).
|
||||
You should use the GitLab for Jira Cloud app because data is synchronized
|
||||
in real time. The Jira DVCS connector updates data only once per hour.
|
||||
|
|
@ -79,7 +79,7 @@ To create an OAuth application:
|
|||
1. Select **Save application**.
|
||||
1. Copy the **Application ID** value.
|
||||
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
|
||||
1. Expand the **GitLab for Jira App** section.
|
||||
1. Expand **GitLab for Jira App**.
|
||||
1. Paste the **Application ID** value into **Jira Connect Application ID**.
|
||||
1. Select **Save changes**.
|
||||
1. Optional. Enable the `jira_connect_oauth` [feature flag](../../administration/feature_flags.md) to avoid [authentication problems in some browsers](#browser-displays-a-sign-in-message-when-already-signed-in).
|
||||
|
|
@ -113,7 +113,7 @@ To set up your self-managed instance for the GitLab for Jira Cloud app in GitLab
|
|||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
|
||||
1. Expand the **GitLab for Jira App** section.
|
||||
1. Expand **GitLab for Jira App**.
|
||||
1. In **Jira Connect Proxy URL**, enter `https://gitlab.com`.
|
||||
1. Select **Save changes**.
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ To configure your GitLab instance to serve as a proxy:
|
|||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
|
||||
1. Expand the **GitLab for Jira App** section.
|
||||
1. Expand **GitLab for Jira App**.
|
||||
1. Select **Enable public key storage**.
|
||||
1. Select **Save changes**.
|
||||
1. [Install the GitLab for Jira Cloud app manually](#install-the-gitlab-for-jira-cloud-app-manually).
|
||||
|
|
@ -270,7 +270,7 @@ You might get an error if you have installed the GitLab for Jira Cloud app from
|
|||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
|
||||
1. Expand the **GitLab for Jira App** section.
|
||||
1. Expand **GitLab for Jira App**.
|
||||
1. Clear the **Jira Connect Proxy URL** text box.
|
||||
1. Select **Save changes**.
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ To resolve this issue on GitLab self-managed, follow one of the solutions below,
|
|||
- In GitLab 14.9 and later:
|
||||
- Contact the [Jira Software Cloud support](https://support.atlassian.com/jira-software-cloud/) and ask to trigger a new installed lifecycle event for the GitLab for Jira Cloud app in your namespace.
|
||||
- In all GitLab versions:
|
||||
- Re-install the GitLab for Jira Cloud app. This might remove all already synced development panel data.
|
||||
- Re-install the GitLab for Jira Cloud app. This method might remove all synced data from the Jira development panel.
|
||||
|
||||
### `Failed to update GitLab version` error when setting up the GitLab for Jira Cloud app for self-managed instances
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ or with a [user-owned application](../../oauth_provider.md#create-a-user-owned-a
|
|||
1. In the **Redirect URI** text box, enter the generated **Redirect URL** from
|
||||
[linking GitLab accounts](https://confluence.atlassian.com/adminjiraserver/linking-gitlab-accounts-1027142272.html).
|
||||
1. In **Scopes**, select `api` and clear any other checkboxes.
|
||||
The DVCS connector requires a **write-enabled** `api` scope to automatically create and manage required webhooks.
|
||||
The Jira DVCS connector requires a **write-enabled** `api` scope to automatically create and manage required webhooks.
|
||||
1. Select **Submit**.
|
||||
1. Copy the **Application ID** and **Secret** values.
|
||||
You need these values to configure Jira.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Troubleshooting Jira DVCS connector **(FREE)**
|
||||
|
||||
Refer to the items in this section if you're having problems with your DVCS connector.
|
||||
Refer to the items in this section if you're having problems with your Jira DVCS connector.
|
||||
|
||||
## Jira cannot access GitLab server
|
||||
|
||||
|
|
@ -32,12 +32,12 @@ Problems with SSL and TLS can cause this error message:
|
|||
Error obtaining access token. Cannot access https://gitlab.example.com from Jira.
|
||||
```
|
||||
|
||||
- The [GitLab Jira integration](../index.md) requires
|
||||
- The [Jira integration](../index.md) requires
|
||||
GitLab to connect to Jira. Any TLS issues that arise from a private certificate
|
||||
authority or self-signed certificate are resolved
|
||||
[on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates),
|
||||
as GitLab is the TLS client.
|
||||
- The Jira Development panel integration requires Jira to connect to GitLab, which
|
||||
- The Jira development panel requires Jira to connect to GitLab, which
|
||||
causes Jira to be the TLS client. If your GitLab server's certificate is not
|
||||
issued by a public certificate authority, add the appropriate certificate
|
||||
(such as your organization's root certificate) to the Java Truststore on Jira Server.
|
||||
|
|
@ -125,7 +125,7 @@ For more information, see the
|
|||
|
||||
## `Sync Failed` error when refreshing repository data
|
||||
|
||||
If you get a `Sync Failed` error in Jira when [refreshing repository data](index.md#refresh-data-imported-to-jira) for specific projects, check your DVCS connector logs. Look for errors that occur when executing requests to API resources in GitLab. For example:
|
||||
If you get a `Sync Failed` error in Jira when [refreshing repository data](index.md#refresh-data-imported-to-jira) for specific projects, check your Jira DVCS connector logs. Look for errors that occur when executing requests to API resources in GitLab. For example:
|
||||
|
||||
```plaintext
|
||||
Failed to execute request [https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 GET https://gitlab.com/api/v4/projects/:id/merge_requests?page=1&per_page=100 returned a response status of 403 Forbidden] errors:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ If your organization uses [Jira](https://www.atlassian.com/software/jira) issues
|
|||
you can [migrate your issues from Jira](../../user/project/import/jira.md) and work
|
||||
exclusively in GitLab. However, if you'd like to continue to use Jira, you can
|
||||
integrate it with GitLab. GitLab offers two types of Jira integrations, and you
|
||||
can use one or both depending on the capabilities you need. We recommend you enable both.
|
||||
can use one or both depending on the capabilities you need.
|
||||
|
||||
## Compare integrations
|
||||
|
||||
|
|
@ -19,29 +19,25 @@ in your GitLab project with any of your projects in Jira.
|
|||
|
||||
### Jira integration
|
||||
|
||||
This integration connects one or more GitLab projects to a Jira instance. You can host
|
||||
The Jira integration connects one or more GitLab projects to a Jira instance. You can host
|
||||
the Jira instance yourself or in [Atlassian Cloud](https://www.atlassian.com/migration/assess/why-cloud).
|
||||
The supported Jira versions are `6.x`, `7.x`, `8.x`, and `9.x`.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, see [Agile Management - GitLab-Jira Basic Integration](https://www.youtube.com/watch?v=fWvwkx5_00E&feature=youtu.be).
|
||||
For more information, see [Jira integration](configure.md).
|
||||
|
||||
To set up the integration, [configure the settings](configure.md) in GitLab.
|
||||
### Jira development panel
|
||||
|
||||
### Jira development panel integration
|
||||
The [Jira development panel](development_panel.md) connects all GitLab projects in a group or personal namespace.
|
||||
When you configure the Jira development panel, you can
|
||||
[view GitLab activity for an issue](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/)
|
||||
including related branches, commits, and merge requests.
|
||||
|
||||
The [Jira development panel integration](development_panel.md)
|
||||
connects all GitLab projects under a group or personal namespace. When configured,
|
||||
relevant GitLab information, including related branches, commits, and merge requests,
|
||||
displays in the [development panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/).
|
||||
|
||||
To set up the Jira development panel integration, use the GitLab for Jira Cloud app
|
||||
or the Jira DVCS (distributed version control system) connector,
|
||||
[depending on your installation](development_panel.md#configure-the-panel).
|
||||
To set up the Jira development panel, use the GitLab for Jira Cloud app
|
||||
or the Jira DVCS connector. For more information, see [Configure the panel](development_panel.md#configure-the-panel).
|
||||
|
||||
### Direct feature comparison
|
||||
|
||||
| Capability | Jira integration | Jira development panel integration |
|
||||
| Capability | Jira integration | Jira development panel |
|
||||
|-|-|-|
|
||||
| Mention a Jira issue ID in a GitLab commit or merge request, and a link to the Jira issue is created. | Yes. | No. |
|
||||
| Mention a Jira issue ID in GitLab and the Jira issue shows the GitLab issue or merge request. | Yes. A Jira comment with the GitLab issue or MR title links to GitLab. The first mention is also added to the Jira issue under **Web links**. | Yes, in the issue's [development panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/). |
|
||||
|
|
@ -72,8 +68,8 @@ All Jira integrations share data with Jira to make it visible outside of GitLab.
|
|||
If you integrate a private GitLab project with Jira, the private data is
|
||||
shared with users who have access to your Jira project.
|
||||
|
||||
The [**Jira project integration**](#jira-integration) posts GitLab data in the form of comments in Jira issues.
|
||||
The GitLab for Jira Cloud app and Jira DVCS connector share this data through the [**Jira Development Panel**](development_panel.md).
|
||||
The [Jira integration](#jira-integration) posts GitLab data in the form of comments in Jira issues.
|
||||
The GitLab for Jira Cloud app and Jira DVCS connector share this data through the [Jira development panel](development_panel.md).
|
||||
This method provides more fine-grained access control because access can be restricted to certain user groups or roles.
|
||||
|
||||
## Third-party Jira integrations
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ module API
|
|||
end
|
||||
|
||||
authenticate_with do |accept|
|
||||
accept.token_types(:personal_access_token, :deploy_token, :job_token)
|
||||
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
|
||||
.sent_through(:http_basic_auth)
|
||||
end
|
||||
|
||||
|
|
@ -126,7 +126,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'Release.gpg' do
|
||||
distribution_from!(project_or_group).file_signature
|
||||
end
|
||||
|
|
@ -145,7 +144,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'Release' do
|
||||
distribution = distribution_from!(project_or_group)
|
||||
track_debian_package_event LIST_PACKAGE
|
||||
|
|
@ -166,7 +164,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'InRelease' do
|
||||
distribution = distribution_from!(project_or_group)
|
||||
track_debian_package_event LIST_PACKAGE
|
||||
|
|
@ -200,7 +197,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'Packages' do
|
||||
present_index_file!(:di_packages)
|
||||
end
|
||||
|
|
@ -222,7 +218,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:di_packages)
|
||||
end
|
||||
|
|
@ -246,7 +241,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'Sources' do
|
||||
present_index_file!(:sources)
|
||||
end
|
||||
|
|
@ -268,7 +262,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:sources)
|
||||
end
|
||||
|
|
@ -296,7 +289,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'Packages' do
|
||||
present_index_file!(:packages)
|
||||
end
|
||||
|
|
@ -318,7 +310,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'by-hash/SHA256/:file_sha256' do
|
||||
present_index_file!(:packages)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,16 +9,16 @@ module API
|
|||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
helpers do
|
||||
def project_or_group
|
||||
user_group
|
||||
find_authorized_group!
|
||||
end
|
||||
end
|
||||
|
||||
after_validation do
|
||||
require_packages_enabled!
|
||||
|
||||
not_found! unless ::Feature.enabled?(:debian_group_packages, user_group)
|
||||
not_found! unless ::Feature.enabled?(:debian_group_packages, project_or_group)
|
||||
|
||||
authorize_read_package!(user_group)
|
||||
authorize_read_package!(project_or_group)
|
||||
end
|
||||
|
||||
params do
|
||||
|
|
@ -45,7 +45,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
|
||||
present_distribution_package_file!(find_project!(params[:project_id]))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module API
|
|||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
helpers do
|
||||
def project_or_group
|
||||
user_project(action: :read_package)
|
||||
authorized_user_project(action: :read_package)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -52,7 +52,6 @@ module API
|
|||
tags %w[debian_packages]
|
||||
end
|
||||
|
||||
route_setting :authentication, authenticate_non_public: true
|
||||
get 'pool/:distribution/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
|
||||
present_distribution_package_file!(project_or_group)
|
||||
end
|
||||
|
|
@ -85,10 +84,9 @@ module API
|
|||
requires :file_name, type: String, desc: 'The filename', regexp: { value: Gitlab::Regex.debian_direct_upload_filename_regex, message: 'Only debs, udebs and ddebs can be directly added to a distribution' }
|
||||
end
|
||||
end
|
||||
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
|
||||
put do
|
||||
authorize_upload!(authorized_user_project)
|
||||
bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:debian_max_file_size, params[:file].size)
|
||||
authorize_upload!(project_or_group)
|
||||
bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:debian_max_file_size, params[:file].size)
|
||||
|
||||
file_params = {
|
||||
file: params['file'],
|
||||
|
|
@ -101,10 +99,10 @@ module API
|
|||
|
||||
package = if params[:distribution].present?
|
||||
::Packages::CreateTemporaryPackageService.new(
|
||||
authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job)
|
||||
project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
|
||||
).execute(:debian, name: ::Packages::Debian::TEMPORARY_PACKAGE_NAME)
|
||||
else
|
||||
::Packages::Debian::FindOrCreateIncomingService.new(authorized_user_project, current_user).execute
|
||||
::Packages::Debian::FindOrCreateIncomingService.new(project_or_group, current_user).execute
|
||||
end
|
||||
|
||||
::Packages::Debian::CreatePackageFileService.new(package: package, current_user: current_user, params: file_params).execute
|
||||
|
|
@ -113,7 +111,7 @@ module API
|
|||
|
||||
created!
|
||||
rescue ObjectStorage::RemoteStoreError => e
|
||||
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id })
|
||||
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
|
||||
|
||||
forbidden!
|
||||
end
|
||||
|
|
@ -137,11 +135,10 @@ module API
|
|||
requires :file_name, type: String, desc: 'The filename', regexp: { value: Gitlab::Regex.debian_direct_upload_filename_regex, message: 'Only debs, udebs and ddebs can be directly added to a distribution' }
|
||||
end
|
||||
end
|
||||
route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true
|
||||
put 'authorize' do
|
||||
authorize_workhorse!(
|
||||
subject: authorized_user_project,
|
||||
maximum_size: authorized_user_project.actual_limits.debian_max_file_size
|
||||
subject: project_or_group,
|
||||
maximum_size: project_or_group.actual_limits.debian_max_file_size
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,33 +20,28 @@ module Gitlab
|
|||
end
|
||||
|
||||
def perform
|
||||
each_sub_batch do |sub_batch|
|
||||
migrate_remediations(sub_batch)
|
||||
each_sub_batch(batching_scope: ->(relation) { relation.select(:id, :raw_metadata) }) do |findings|
|
||||
migrate_remediations(
|
||||
findings,
|
||||
Link
|
||||
.where(vulnerability_occurrence_id: findings.map(&:id))
|
||||
.group(:vulnerability_occurrence_id, :name, :url)
|
||||
.count
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def migrate_remediations(sub_batch)
|
||||
sub_batch.each do |finding|
|
||||
links = extract_links(finding.raw_metadata)
|
||||
|
||||
list_of_attrs = links.map do |link|
|
||||
build_link(finding, link)
|
||||
end
|
||||
|
||||
next unless list_of_attrs.present?
|
||||
|
||||
begin
|
||||
create_links(list_of_attrs)
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
rescue StandardError => e
|
||||
logger.error(
|
||||
message: e.message,
|
||||
class: self.class.name,
|
||||
model_id: finding.id
|
||||
)
|
||||
end
|
||||
def migrate_remediations(findings, existing_links)
|
||||
findings.each do |finding|
|
||||
create_links(build_links_from(finding, existing_links))
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
logger.error(
|
||||
message: e.message,
|
||||
class: self.class.name,
|
||||
model_id: finding.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -61,7 +56,16 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def build_links_from(finding, existing_links)
|
||||
extract_links(finding.raw_metadata).filter_map do |link|
|
||||
key = [finding.id, link['name'], link['url']]
|
||||
build_link(finding, link) unless existing_links.key?(key)
|
||||
end
|
||||
end
|
||||
|
||||
def create_links(attributes)
|
||||
return if attributes.empty?
|
||||
|
||||
Link.upsert_all(attributes, returning: false)
|
||||
end
|
||||
|
||||
|
|
@ -72,6 +76,12 @@ module Gitlab
|
|||
return [] if parsed_links.blank?
|
||||
|
||||
parsed_links.select { |link| link.try(:[], 'url').present? }.uniq
|
||||
rescue JSON::ParserError => e
|
||||
logger.warn(
|
||||
message: e.message,
|
||||
class: self.class.name
|
||||
)
|
||||
[]
|
||||
end
|
||||
|
||||
def logger
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def prefixed_error_message(message, prefix)
|
||||
"#{prefix}: #{message}"
|
||||
"#{prefix} #{message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18729,6 +18729,9 @@ msgstr ""
|
|||
msgid "ForksDivergence|Update fork"
|
||||
msgstr ""
|
||||
|
||||
msgid "ForksDivergence|View merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "Format: %{dateFormat}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -31270,7 +31273,7 @@ msgstr ""
|
|||
msgid "PackageRegistry|Deleting the last package asset will remove version %{version} of %{name}. Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete the package anyway? %{docLinkStart}What are the risks?%{docLinkEnd}"
|
||||
msgid "PackageRegistry|Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete %{name} version %{version} anyway? %{docLinkStart}What are the risks?%{docLinkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Duplicate packages"
|
||||
|
|
|
|||
|
|
@ -59,12 +59,12 @@
|
|||
"@gitlab/svgs": "3.38.0",
|
||||
"@gitlab/ui": "60.1.0",
|
||||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230407181558",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230418150125",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"@rails/actioncable": "6.1.4-7",
|
||||
"@rails/ujs": "6.1.4-7",
|
||||
"@sourcegraph/code-host-integration": "0.0.84",
|
||||
"@sourcegraph/code-host-integration": "0.0.91",
|
||||
"@tiptap/core": "^2.0.3",
|
||||
"@tiptap/extension-blockquote": "^2.0.3",
|
||||
"@tiptap/extension-bold": "^2.0.3",
|
||||
|
|
|
|||
|
|
@ -510,6 +510,33 @@ RSpec.describe 'Group', feature_category: :subgroups do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in a private group' do
|
||||
before do
|
||||
group.update!(
|
||||
visibility_level: Gitlab::VisibilityLevel::PRIVATE,
|
||||
project_creation_level: Gitlab::Access::MAINTAINER_PROJECT_ACCESS
|
||||
)
|
||||
end
|
||||
|
||||
context 'when visibility levels have been restricted to private only by an administrator' do
|
||||
before do
|
||||
stub_application_setting(
|
||||
restricted_visibility_levels: [
|
||||
Gitlab::VisibilityLevel::PRIVATE
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not display the "New project" button' do
|
||||
visit group_path(group)
|
||||
|
||||
page.within '[data-testid="group-buttons"]' do
|
||||
expect(page).not_to have_link('New project')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'group README', :js do
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { GlModal as RealGlModal, GlSprintf } from '@gitlab/ui';
|
||||
import { GlModal as RealGlModal, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import {
|
||||
DELETE_PACKAGE_MODAL_PRIMARY_ACTION,
|
||||
DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT,
|
||||
DELETE_PACKAGE_WITH_REQUEST_FORWARDING_PRIMARY_ACTION,
|
||||
DELETE_PACKAGES_REQUEST_FORWARDING_MODAL_CONTENT,
|
||||
DELETE_PACKAGES_WITH_REQUEST_FORWARDING_PRIMARY_ACTION,
|
||||
REQUEST_FORWARDING_HELP_PAGE_PATH,
|
||||
} from '~/packages_and_registries/package_registry/constants';
|
||||
|
||||
const GlModal = stubComponent(RealGlModal, {
|
||||
|
|
@ -21,16 +21,17 @@ describe('DeleteModal', () => {
|
|||
|
||||
const defaultItemsToBeDeleted = [
|
||||
{
|
||||
name: 'package 01',
|
||||
name: 'package-1',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
name: 'package 02',
|
||||
name: 'package-2',
|
||||
version: '1.0.0',
|
||||
},
|
||||
];
|
||||
|
||||
const findModal = () => wrapper.findComponent(GlModal);
|
||||
const findLink = () => wrapper.findComponent(GlLink);
|
||||
|
||||
const mountComponent = ({
|
||||
itemsToBeDeleted = defaultItemsToBeDeleted,
|
||||
|
|
@ -73,7 +74,7 @@ describe('DeleteModal', () => {
|
|||
mountComponent({ itemsToBeDeleted: [defaultItemsToBeDeleted[0]] });
|
||||
|
||||
expect(findModal().text()).toMatchInterpolatedText(
|
||||
'You are about to delete version 1.0.0 of package 01. Are you sure?',
|
||||
'You are about to delete version 1.0.0 of package-1. Are you sure?',
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -92,6 +93,13 @@ describe('DeleteModal', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('contains link to help page', () => {
|
||||
mountComponent({ showRequestForwardingContent: true });
|
||||
|
||||
expect(findLink().exists()).toBe(true);
|
||||
expect(findLink().attributes('href')).toBe(REQUEST_FORWARDING_HELP_PAGE_PATH);
|
||||
});
|
||||
|
||||
it('sets the right action primary text', () => {
|
||||
mountComponent({ showRequestForwardingContent: true });
|
||||
|
||||
|
|
@ -110,10 +118,15 @@ describe('DeleteModal', () => {
|
|||
|
||||
it('renders correct description', () => {
|
||||
expect(findModal().text()).toMatchInterpolatedText(
|
||||
DELETE_PACKAGE_REQUEST_FORWARDING_MODAL_CONTENT,
|
||||
'Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete package-1 version 1.0.0 anyway? What are the risks?',
|
||||
);
|
||||
});
|
||||
|
||||
it('contains link to help page', () => {
|
||||
expect(findLink().exists()).toBe(true);
|
||||
expect(findLink().attributes('href')).toBe(REQUEST_FORWARDING_HELP_PAGE_PATH);
|
||||
});
|
||||
|
||||
it('sets the right action primary text', () => {
|
||||
expect(findModal().props('actionPrimary')).toMatchObject({
|
||||
text: DELETE_PACKAGE_WITH_REQUEST_FORWARDING_PRIMARY_ACTION,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import createMockApollo from 'helpers/mock_apollo_helper';
|
|||
import { stubComponent } from 'helpers/stub_component';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
|
||||
import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue';
|
||||
import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue';
|
||||
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
|
||||
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
|
||||
|
|
@ -46,7 +45,6 @@ describe('PackageVersionsList', () => {
|
|||
findListRow: () => wrapper.findComponent(VersionRow),
|
||||
findAllListRow: () => wrapper.findAllComponents(VersionRow),
|
||||
findDeletePackagesModal: () => wrapper.findComponent(DeleteModal),
|
||||
findPackageListDeleteModal: () => wrapper.findComponent(DeletePackageModal),
|
||||
};
|
||||
|
||||
const mountComponent = ({
|
||||
|
|
@ -247,7 +245,7 @@ describe('PackageVersionsList', () => {
|
|||
`('$description', ({ finderFunction, deletePayload }) => {
|
||||
let eventSpy;
|
||||
const category = 'UI::NpmPackages';
|
||||
const { findPackageListDeleteModal } = uiElements;
|
||||
const { findDeletePackagesModal } = uiElements;
|
||||
|
||||
beforeEach(async () => {
|
||||
eventSpy = jest.spyOn(Tracking, 'event');
|
||||
|
|
@ -256,10 +254,10 @@ describe('PackageVersionsList', () => {
|
|||
finderFunction().vm.$emit('delete', deletePayload);
|
||||
});
|
||||
|
||||
it('passes itemToBeDeleted to the modal', () => {
|
||||
expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual(
|
||||
it('passes itemsToBeDeleted to the modal', () => {
|
||||
expect(findDeletePackagesModal().props('itemsToBeDeleted')).toStrictEqual([
|
||||
packageVersions()[0],
|
||||
);
|
||||
]);
|
||||
});
|
||||
|
||||
it('requesting delete tracks the right action', () => {
|
||||
|
|
@ -272,7 +270,7 @@ describe('PackageVersionsList', () => {
|
|||
|
||||
describe('when modal confirms', () => {
|
||||
beforeEach(() => {
|
||||
findPackageListDeleteModal().vm.$emit('ok');
|
||||
findDeletePackagesModal().vm.$emit('confirm');
|
||||
});
|
||||
|
||||
it('emits delete when modal confirms', () => {
|
||||
|
|
@ -288,14 +286,14 @@ describe('PackageVersionsList', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it.each(['ok', 'cancel'])('resets itemToBeDeleted when modal emits %s', async (event) => {
|
||||
await findPackageListDeleteModal().vm.$emit(event);
|
||||
it.each(['confirm', 'cancel'])('resets itemsToBeDeleted when modal emits %s', async (event) => {
|
||||
await findDeletePackagesModal().vm.$emit(event);
|
||||
|
||||
expect(findPackageListDeleteModal().props('itemToBeDeleted')).toBeNull();
|
||||
expect(findDeletePackagesModal().props('itemsToBeDeleted')).toEqual([]);
|
||||
});
|
||||
|
||||
it('canceling delete tracks the right action', () => {
|
||||
findPackageListDeleteModal().vm.$emit('cancel');
|
||||
findDeletePackagesModal().vm.$emit('cancel');
|
||||
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
category,
|
||||
|
|
@ -383,4 +381,15 @@ describe('PackageVersionsList', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with isRequestForwardingEnabled prop', () => {
|
||||
const { findDeletePackagesModal } = uiElements;
|
||||
|
||||
it.each([true, false])('sets modal prop showRequestForwardingContent to %s', async (value) => {
|
||||
mountComponent({ props: { isRequestForwardingEnabled: value } });
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDeletePackagesModal().props('showRequestForwardingContent')).toBe(value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const packagePipelines = (extend) => [
|
|||
ref: 'master',
|
||||
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
|
||||
project: {
|
||||
id: '1',
|
||||
id: '14',
|
||||
name: 'project14',
|
||||
webUrl: 'http://gdk.test:3000/namespace14/project14',
|
||||
__typename: 'Project',
|
||||
|
|
@ -219,7 +219,10 @@ export const pagination = (extend) => ({
|
|||
...extend,
|
||||
});
|
||||
|
||||
export const packageDetailsQuery = (extendPackage) => ({
|
||||
export const packageDetailsQuery = ({
|
||||
extendPackage = {},
|
||||
packageSettings = defaultPackageGroupSettings,
|
||||
} = {}) => ({
|
||||
data: {
|
||||
package: {
|
||||
...packageData(),
|
||||
|
|
@ -235,6 +238,12 @@ export const packageDetailsQuery = (extendPackage) => ({
|
|||
path: 'projectPath',
|
||||
name: 'gitlab-test',
|
||||
fullPath: 'gitlab-test',
|
||||
group: {
|
||||
id: '1',
|
||||
packageSettings,
|
||||
__typename: 'Group',
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
tags: {
|
||||
nodes: packageTags(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlEmptyState, GlModal, GlTabs, GlTab, GlSprintf } from '@gitlab/ui';
|
||||
import { GlEmptyState, GlModal, GlTabs, GlTab, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
|
@ -18,6 +18,7 @@ import PackageTitle from '~/packages_and_registries/package_registry/components/
|
|||
import DeletePackages from '~/packages_and_registries/package_registry/components/functional/delete_packages.vue';
|
||||
import PackageVersionsList from '~/packages_and_registries/package_registry/components/details/package_versions_list.vue';
|
||||
import {
|
||||
REQUEST_FORWARDING_HELP_PAGE_PATH,
|
||||
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE,
|
||||
PACKAGE_TYPE_COMPOSER,
|
||||
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
|
||||
|
|
@ -43,6 +44,7 @@ import {
|
|||
packageFiles,
|
||||
packageDestroyFilesMutation,
|
||||
packageDestroyFilesMutationError,
|
||||
defaultPackageGroupSettings,
|
||||
} from '../mock_data';
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
|
@ -125,6 +127,7 @@ describe('PackagesApp', () => {
|
|||
const findDependencyRows = () => wrapper.findAllComponents(DependencyRow);
|
||||
const findDeletePackageModal = () => wrapper.findAllComponents(DeletePackages).at(1);
|
||||
const findDeletePackages = () => wrapper.findComponent(DeletePackages);
|
||||
const findLink = () => wrapper.findComponent(GlLink);
|
||||
|
||||
it('renders an empty state component', async () => {
|
||||
createComponent({ resolver: jest.fn().mockResolvedValue(emptyPackageDetailsQuery) });
|
||||
|
|
@ -184,7 +187,9 @@ describe('PackagesApp', () => {
|
|||
createComponent({
|
||||
resolver: jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageType,
|
||||
extendPackage: {
|
||||
packageType,
|
||||
},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
|
@ -239,16 +244,55 @@ describe('PackagesApp', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('shows the delete confirmation modal when delete is clicked', async () => {
|
||||
createComponent();
|
||||
describe('when delete button is clicked', () => {
|
||||
describe('with request forwarding enabled', () => {
|
||||
beforeEach(async () => {
|
||||
const resolver = jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageSettings: {
|
||||
...defaultPackageGroupSettings,
|
||||
npmPackageRequestsForwarding: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
createComponent({ resolver });
|
||||
|
||||
await waitForPromises();
|
||||
await waitForPromises();
|
||||
|
||||
await findDeleteButton().trigger('click');
|
||||
await findDeleteButton().trigger('click');
|
||||
});
|
||||
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'You are about to delete version 1.0.0 of @gitlab-org/package-15. Are you sure?',
|
||||
);
|
||||
it('shows the delete confirmation modal with request forwarding content', () => {
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'Deleting this package while request forwarding is enabled for the project can pose a security risk. Do you want to delete @gitlab-org/package-15 version 1.0.0 anyway? What are the risks?',
|
||||
);
|
||||
});
|
||||
|
||||
it('contains link to help page', () => {
|
||||
expect(findLink().exists()).toBe(true);
|
||||
expect(findLink().attributes('href')).toBe(REQUEST_FORWARDING_HELP_PAGE_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the delete confirmation modal without request forwarding content', async () => {
|
||||
const resolver = jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageSettings: {
|
||||
...defaultPackageGroupSettings,
|
||||
npmPackageRequestsForwarding: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
createComponent({ resolver });
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
await findDeleteButton().trigger('click');
|
||||
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'You are about to delete version 1.0.0 of @gitlab-org/package-15. Are you sure?',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('successful request', () => {
|
||||
|
|
@ -302,7 +346,9 @@ describe('PackagesApp', () => {
|
|||
createComponent({
|
||||
resolver: jest
|
||||
.fn()
|
||||
.mockResolvedValue(packageDetailsQuery({ packageType: PACKAGE_TYPE_COMPOSER })),
|
||||
.mockResolvedValue(
|
||||
packageDetailsQuery({ extendPackage: { packageType: PACKAGE_TYPE_COMPOSER } }),
|
||||
),
|
||||
});
|
||||
|
||||
await waitForPromises();
|
||||
|
|
@ -341,12 +387,18 @@ describe('PackagesApp', () => {
|
|||
const [packageFile] = packageFiles();
|
||||
const resolver = jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageFiles: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
extendPackage: {
|
||||
packageFiles: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
nodes: [packageFile],
|
||||
__typename: 'PackageFileConnection',
|
||||
},
|
||||
nodes: [packageFile],
|
||||
__typename: 'PackageFileConnection',
|
||||
},
|
||||
packageSettings: {
|
||||
...defaultPackageGroupSettings,
|
||||
npmPackageRequestsForwarding: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -523,11 +575,17 @@ describe('PackagesApp', () => {
|
|||
it('opens the delete package confirmation modal', async () => {
|
||||
const resolver = jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageFiles: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
extendPackage: {
|
||||
packageFiles: {
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
},
|
||||
nodes: packageFiles(),
|
||||
},
|
||||
nodes: packageFiles(),
|
||||
},
|
||||
packageSettings: {
|
||||
...defaultPackageGroupSettings,
|
||||
npmPackageRequestsForwarding: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -565,8 +623,10 @@ describe('PackagesApp', () => {
|
|||
createComponent({
|
||||
resolver: jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
versions: {
|
||||
count: 0,
|
||||
extendPackage: {
|
||||
versions: {
|
||||
count: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
|
@ -591,6 +651,7 @@ describe('PackagesApp', () => {
|
|||
count: packageVersions().length,
|
||||
isMutationLoading: false,
|
||||
packageId: 'gid://gitlab/Packages::Package/1',
|
||||
isRequestForwardingEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -646,8 +707,10 @@ describe('PackagesApp', () => {
|
|||
createComponent({
|
||||
resolver: jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageType: PACKAGE_TYPE_NUGET,
|
||||
dependencyLinks: { nodes: [] },
|
||||
extendPackage: {
|
||||
packageType: PACKAGE_TYPE_NUGET,
|
||||
dependencyLinks: { nodes: [] },
|
||||
},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
|
@ -663,7 +726,9 @@ describe('PackagesApp', () => {
|
|||
createComponent({
|
||||
resolver: jest.fn().mockResolvedValue(
|
||||
packageDetailsQuery({
|
||||
packageType: PACKAGE_TYPE_NUGET,
|
||||
extendPackage: {
|
||||
packageType: PACKAGE_TYPE_NUGET,
|
||||
},
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ describe('ForkInfo component', () => {
|
|||
const findIcon = () => wrapper.findComponent(GlIcon);
|
||||
const findUpdateForkButton = () => wrapper.findByTestId('update-fork-button');
|
||||
const findCreateMrButton = () => wrapper.findByTestId('create-mr-button');
|
||||
const findViewMrButton = () => wrapper.findByTestId('view-mr-button');
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findDivergenceMessage = () => wrapper.findByTestId('divergence-message');
|
||||
const findInaccessibleMessage = () => wrapper.findByTestId('inaccessible-project');
|
||||
|
|
@ -145,8 +146,14 @@ describe('ForkInfo component', () => {
|
|||
expect(findCreateMrButton().attributes('href')).toBe(propsForkInfo.createMrPath);
|
||||
});
|
||||
|
||||
it('does not render create MR button if user had no permission to Create MR in fork', async () => {
|
||||
await createComponent({ canUserCreateMrInFork: false });
|
||||
it('renders View MR Button with correct path', async () => {
|
||||
const viewMrPath = 'path/to/view/mr';
|
||||
await createComponent({ viewMrPath });
|
||||
expect(findViewMrButton().attributes('href')).toBe(viewMrPath);
|
||||
});
|
||||
|
||||
it('does not render create MR button if create MR path is blank', async () => {
|
||||
await createComponent({ createMrPath: '' });
|
||||
expect(findCreateMrButton().exists()).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ export const propsForkInfo = {
|
|||
aheadComparePath: '/nataliia/myGitLab/-/compare/main...ref?from_project_id=1',
|
||||
behindComparePath: 'gitlab-org/gitlab/-/compare/ref...main?from_project_id=2',
|
||||
createMrPath: 'path/to/new/mr',
|
||||
canUserCreateMrInFork: true,
|
||||
};
|
||||
|
||||
export const propsConflictsModal = {
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ RSpec.describe GitlabSchema.types['Project'] do
|
|||
let_it_be(:project) { create(:project_empty_repo) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect(subject['errors'][0]['message']).to eq('UF: You must <a target="_blank" rel="noopener noreferrer" ' \
|
||||
expect(subject['errors'][0]['message']).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \
|
||||
'href="http://localhost/help/user/project/repository/index.md#' \
|
||||
'add-files-to-a-repository">add at least one file to the ' \
|
||||
'repository</a> before using Security features.')
|
||||
|
|
|
|||
|
|
@ -1359,11 +1359,16 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
|
|||
end
|
||||
|
||||
context 'when fork source is available' do
|
||||
it 'returns the data related to fork divergence' do
|
||||
source_project = project_with_repo
|
||||
let_it_be(:fork_network) { create(:fork_network, root_project: project_with_repo) }
|
||||
let_it_be(:source_project) { project_with_repo }
|
||||
|
||||
allow(helper).to receive(:visible_fork_source).with(project).and_return(source_project)
|
||||
allow(helper).to receive(:can_user_create_mr_in_fork).with(source_project).and_return(false)
|
||||
before_all do
|
||||
project.fork_network = fork_network
|
||||
project.add_developer(user)
|
||||
source_project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns the data related to fork divergence' do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
|
||||
ahead_path =
|
||||
|
|
@ -1382,9 +1387,44 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
|
|||
behind_compare_path: behind_path,
|
||||
source_default_branch: source_project.default_branch,
|
||||
create_mr_path: create_mr_path,
|
||||
can_user_create_mr_in_fork: false
|
||||
view_mr_path: nil
|
||||
})
|
||||
end
|
||||
|
||||
it 'returns view_mr_path if a merge request for the branch exists' do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
|
||||
merge_request =
|
||||
create(:merge_request, source_project: project, target_project: project_with_repo,
|
||||
source_branch: 'ref', target_branch: project_with_repo.default_branch)
|
||||
|
||||
expect(helper.vue_fork_divergence_data(project, 'ref')).to include({
|
||||
create_mr_path: nil,
|
||||
view_mr_path: "/#{source_project.full_path}/-/merge_requests/#{merge_request.iid}"
|
||||
})
|
||||
end
|
||||
|
||||
context 'when a user cannot create a merge request' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:project_role, :source_project_role) do
|
||||
:guest | :developer
|
||||
:developer | :guest
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'create_mr_path is nil' do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
|
||||
project.add_member(user, project_role)
|
||||
source_project.add_member(user, source_project_role)
|
||||
|
||||
expect(helper.vue_fork_divergence_data(project, 'ref')).to include({
|
||||
create_mr_path: nil, view_mr_path: nil
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -139,33 +139,18 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateLinksForVulnerabilityFindings
|
|||
|
||||
expect { perform_migration }.to change { vulnerability_finding_links.count }.by(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when create throws exception ActiveRecord::RecordNotUnique' do
|
||||
before do
|
||||
allow(migration).to receive(:create_links).and_raise(ActiveRecord::RecordNotUnique)
|
||||
end
|
||||
|
||||
it 'does not log this error nor create new records' do
|
||||
expect(Gitlab::AppLogger).not_to receive(:error)
|
||||
|
||||
expect { perform_migration }.not_to change { vulnerability_finding_links.count }
|
||||
end
|
||||
context 'when Gitlab::Json throws exception JSON::ParserError' do
|
||||
before do
|
||||
create_finding!(project1.id, scanner1.id, { links: [link_hash] })
|
||||
allow(Gitlab::Json).to receive(:parse).and_raise(JSON::ParserError)
|
||||
end
|
||||
|
||||
context 'when create throws exception StandardError' do
|
||||
before do
|
||||
allow(migration).to receive(:create_links).and_raise(StandardError)
|
||||
end
|
||||
it 'does not log this error nor create new records' do
|
||||
expect(Gitlab::AppLogger).not_to receive(:error)
|
||||
|
||||
it 'logs StandardError' do
|
||||
expect(Gitlab::AppLogger).to receive(:error).with({
|
||||
class: described_class.name, message: StandardError.to_s, model_id: finding1.id
|
||||
})
|
||||
expect(Gitlab::AppLogger).to receive(:error).with({
|
||||
class: described_class.name, message: StandardError.to_s, model_id: finding2.id
|
||||
})
|
||||
expect { perform_migration }.not_to change { vulnerability_finding_links.count }
|
||||
end
|
||||
expect { perform_migration }.not_to change { vulnerability_finding_links.count }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -181,6 +166,10 @@ RSpec.describe Gitlab::BackgroundMigration::MigrateLinksForVulnerabilityFindings
|
|||
|
||||
expect { perform_migration }.not_to change { vulnerability_finding_links.count }
|
||||
end
|
||||
|
||||
it 'does not raise ActiveRecord::RecordNotUnique' do
|
||||
expect { perform_migration }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -73,9 +73,8 @@ RSpec.describe Gitlab::BareRepositoryImport::Importer do
|
|||
end
|
||||
|
||||
it 'does not schedule an import' do
|
||||
expect_next_instance_of(Project) do |instance|
|
||||
expect(instance).not_to receive(:import_schedule)
|
||||
end
|
||||
project = Project.find_by_full_path(project_path)
|
||||
expect(project).not_to receive(:import_schedule)
|
||||
|
||||
importer.create_project_if_needed
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ RSpec.describe Gitlab::BitbucketImport::ProjectCreator do
|
|||
end
|
||||
|
||||
it 'creates project' do
|
||||
expect_next_instance_of(Project) do |project|
|
||||
expect(project).to receive(:add_import_job)
|
||||
allow_next_instance_of(Project) do |project|
|
||||
allow(project).to receive(:add_import_job)
|
||||
end
|
||||
|
||||
project_creator = described_class.new(repo, 'vim', namespace, user, access_params)
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ RSpec.describe Gitlab::GitlabImport::ProjectCreator do
|
|||
end
|
||||
|
||||
it 'creates project' do
|
||||
expect_next_instance_of(Project) do |project|
|
||||
expect(project).to receive(:add_import_job)
|
||||
allow_next_instance_of(Project) do |project|
|
||||
allow(project).to receive(:add_import_job)
|
||||
end
|
||||
|
||||
project_creator = described_class.new(repo, namespace, user, access_params)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
|
|||
before do
|
||||
namespace.add_owner(user)
|
||||
|
||||
expect_next_instance_of(Project) do |project|
|
||||
allow_next_instance_of(Project) do |project|
|
||||
allow(project).to receive(:add_import_job)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ RSpec.describe Gitlab::Utils::ErrorMessage, feature_category: :error_tracking do
|
|||
|
||||
describe '#to_user_facing' do
|
||||
it 'returns a user-facing error message with the UF prefix' do
|
||||
expect(described_class.to_user_facing(message)).to eq("UF: #{message}")
|
||||
expect(described_class.to_user_facing(message)).to eq("UF #{message}")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#prefixed_error_message' do
|
||||
it 'returns a message with the given prefix' do
|
||||
prefix = 'ERROR'
|
||||
expect(described_class.prefixed_error_message(message, prefix)).to eq("#{prefix}: #{message}")
|
||||
expect(described_class.prefixed_error_message(message, prefix)).to eq("#{prefix} #{message}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,13 +10,7 @@ RSpec.describe RescheduleMigrationForLinks, :migration, feature_category: :vulne
|
|||
it 'schedules a batched background migration' do
|
||||
migrate!
|
||||
|
||||
expect(migration).to have_scheduled_batched_migration(
|
||||
table_name: :vulnerability_occurrences,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
expect(migration).not_to have_scheduled_batched_migration
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe RescheduleLinksAvoidingDuplication, :migration, feature_category: :vulnerability_management do
|
||||
let(:migration) { described_class::MIGRATION }
|
||||
|
||||
describe '#up' do
|
||||
it 'schedules a batched background migration' do
|
||||
migrate!
|
||||
|
||||
expect(migration).to have_scheduled_batched_migration(
|
||||
table_name: :vulnerability_occurrences,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#down' do
|
||||
it 'deletes all batched migration records' do
|
||||
migrate!
|
||||
schema_migrate_down!
|
||||
|
||||
expect(migration).not_to have_scheduled_batched_migration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe GroupPolicy, feature_category: :system_access do
|
||||
include AdminModeHelper
|
||||
include_context 'GroupPolicy context'
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
context 'public group with no user' do
|
||||
let(:group) { create(:group, :public, :crm_enabled) }
|
||||
|
|
@ -668,6 +669,59 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
|
|||
it { is_expected.to be_allowed(:create_projects) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with visibility levels restricted by the administrator' do
|
||||
let_it_be(:public) { Gitlab::VisibilityLevel::PUBLIC }
|
||||
let_it_be(:internal) { Gitlab::VisibilityLevel::INTERNAL }
|
||||
let_it_be(:private) { Gitlab::VisibilityLevel::PRIVATE }
|
||||
let_it_be(:policy) { :create_projects }
|
||||
|
||||
where(:restricted_visibility_levels, :group_visibility, :can_create_project?) do
|
||||
[] | ref(:public) | true
|
||||
[] | ref(:internal) | true
|
||||
[] | ref(:private) | true
|
||||
[ref(:public)] | ref(:public) | true
|
||||
[ref(:public)] | ref(:internal) | true
|
||||
[ref(:public)] | ref(:private) | true
|
||||
[ref(:internal)] | ref(:public) | true
|
||||
[ref(:internal)] | ref(:internal) | true
|
||||
[ref(:internal)] | ref(:private) | true
|
||||
[ref(:private)] | ref(:public) | true
|
||||
[ref(:private)] | ref(:internal) | true
|
||||
[ref(:private)] | ref(:private) | false
|
||||
[ref(:public), ref(:internal)] | ref(:public) | true
|
||||
[ref(:public), ref(:internal)] | ref(:internal) | true
|
||||
[ref(:public), ref(:internal)] | ref(:private) | true
|
||||
[ref(:public), ref(:private)] | ref(:public) | true
|
||||
[ref(:public), ref(:private)] | ref(:internal) | true
|
||||
[ref(:public), ref(:private)] | ref(:private) | false
|
||||
[ref(:private), ref(:internal)] | ref(:public) | true
|
||||
[ref(:private), ref(:internal)] | ref(:internal) | false
|
||||
[ref(:private), ref(:internal)] | ref(:private) | false
|
||||
[ref(:public), ref(:internal), ref(:private)] | ref(:public) | false
|
||||
[ref(:public), ref(:internal), ref(:private)] | ref(:internal) | false
|
||||
[ref(:public), ref(:internal), ref(:private)] | ref(:private) | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
group.update!(visibility_level: group_visibility)
|
||||
stub_application_setting(restricted_visibility_levels: restricted_visibility_levels)
|
||||
end
|
||||
|
||||
context 'with non-admin user' do
|
||||
let(:current_user) { owner }
|
||||
|
||||
it { is_expected.to(can_create_project? ? be_allowed(policy) : be_disallowed(policy)) }
|
||||
end
|
||||
|
||||
context 'with admin user', :enable_admin_mode do
|
||||
let(:current_user) { admin }
|
||||
|
||||
it { is_expected.to be_allowed(policy) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'import_projects' do
|
||||
|
|
@ -878,7 +932,7 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
|
||||
%w(guest reporter developer maintainer owner).each do |role|
|
||||
%w[guest reporter developer maintainer owner].each do |role|
|
||||
context role do
|
||||
let(:current_user) { send(role) }
|
||||
|
||||
|
|
@ -1046,8 +1100,6 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
|
|||
end
|
||||
|
||||
describe 'observability' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:allowed_admin) { be_allowed(:read_observability) && be_allowed(:admin_observability) }
|
||||
let(:allowed_read) { be_allowed(:read_observability) && be_disallowed(:admin_observability) }
|
||||
let(:disallowed) { be_disallowed(:read_observability) && be_disallowed(:admin_observability) }
|
||||
|
|
@ -1661,8 +1713,6 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
|
|||
|
||||
describe 'read_usage_quotas policy' do
|
||||
context 'reading usage quotas' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:policy) { :read_usage_quotas }
|
||||
|
||||
where(:role, :admin_mode, :allowed) do
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do
|
|||
|
||||
shared_examples 'not a Debian package tracking event' do
|
||||
include_context 'Debian repository access', :public, :developer, :basic do
|
||||
it_behaves_like 'not a package tracking event'
|
||||
it_behaves_like 'not a package tracking event', described_class.name, /.*/
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
|
|||
|
||||
shared_examples 'not a Debian package tracking event' do
|
||||
include_context 'Debian repository access', :public, :developer, :basic do
|
||||
it_behaves_like 'not a package tracking event'
|
||||
it_behaves_like 'not a package tracking event', described_class.name, /.*/
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -180,6 +180,7 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
|
|||
let(:file_name) { 'libsample0_1.2.3~alpha2_amd64.deb' }
|
||||
|
||||
it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil
|
||||
it_behaves_like 'Debian packages endpoint catching ObjectStorage::RemoteStoreError'
|
||||
it_behaves_like 'a Debian package tracking event', 'push_package'
|
||||
|
||||
context 'with codename and component' do
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
|
|||
end
|
||||
|
||||
RSpec.shared_examples 'package details link' do |property|
|
||||
before do
|
||||
stub_application_setting(npm_package_requests_forwarding: false)
|
||||
end
|
||||
|
||||
it 'navigates to the correct url' do
|
||||
page.within(packages_table_selector) do
|
||||
click_link package.name
|
||||
|
|
|
|||
|
|
@ -166,6 +166,18 @@ RSpec.shared_examples 'Debian packages write endpoint' do |desired_behavior, suc
|
|||
it_behaves_like 'rejects Debian access with unknown container id', :unauthorized, :basic
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'Debian packages endpoint catching ObjectStorage::RemoteStoreError' do
|
||||
include_context 'Debian repository access', :public, :developer, :basic do
|
||||
it "returns forbidden" do
|
||||
expect(::Packages::Debian::CreatePackageFileService).to receive(:new).and_raise ObjectStorage::RemoteStoreError
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'Debian packages index endpoint' do |success_body|
|
||||
it_behaves_like 'Debian packages read endpoint', 'GET', :success, success_body
|
||||
|
||||
|
|
|
|||
|
|
@ -161,11 +161,11 @@ RSpec.shared_examples 'a package tracking event' do |category, action, service_p
|
|||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'not a package tracking event' do
|
||||
RSpec.shared_examples 'not a package tracking event' do |category, action|
|
||||
it 'does not create a gitlab tracking event', :snowplow, :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect_no_snowplow_event
|
||||
expect_no_snowplow_event category: category, action: action
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ RSpec.shared_examples_for 'services security ci configuration create service' do
|
|||
it 'returns an error' do
|
||||
expect { result }.to raise_error { |error|
|
||||
expect(error).to be_a(Gitlab::Graphql::Errors::MutationError)
|
||||
expect(error.message).to eq('UF: You must <a target="_blank" rel="noopener noreferrer" ' \
|
||||
expect(error.message).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \
|
||||
'href="http://localhost/help/user/project/repository/index.md' \
|
||||
'#add-files-to-a-repository">add at least one file to the repository' \
|
||||
'</a> before using Security features.')
|
||||
|
|
|
|||
16
yarn.lock
16
yarn.lock
|
|
@ -1134,10 +1134,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
|
||||
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
|
||||
|
||||
"@gitlab/web-ide@0.0.1-dev-20230407181558":
|
||||
version "0.0.1-dev-20230407181558"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230407181558.tgz#69c5243e8c567ea3ef4879ede68566f99d47705a"
|
||||
integrity sha512-8YdZxGvXOX3hU/2F1XSwMWFRz56a/QLvY+amne7ie1NRzIVx6SmjKOxot+F1FhSXjc0zbwooGoOn8j6wkgdHCg==
|
||||
"@gitlab/web-ide@0.0.1-dev-20230418150125":
|
||||
version "0.0.1-dev-20230418150125"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230418150125.tgz#ea1cb4952c41e19f76465379c96967e98a1217ab"
|
||||
integrity sha512-rdh8gsvGY6WAl0pGFGRVJIlKSiVY37NimKdOndUcUc62xiZqdT1+4aBmnIk9XjAromURsyFRD6Z9ABNhYx7SBA==
|
||||
|
||||
"@graphql-eslint/eslint-plugin@3.18.0":
|
||||
version "3.18.0"
|
||||
|
|
@ -1797,10 +1797,10 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@sourcegraph/code-host-integration@0.0.84":
|
||||
version "0.0.84"
|
||||
resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.84.tgz#680f5eadde4d05c1dfccb32e14b0a7e58365ebad"
|
||||
integrity sha512-xHaplY49vBHeXggcc0K3LNQ0VebKy3BwCSokXGxqHDaRJ8JmUwp/DyZX1RFp0Rx/UR6OPXQXKyfVqFcz/UlkUw==
|
||||
"@sourcegraph/code-host-integration@0.0.91":
|
||||
version "0.0.91"
|
||||
resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.91.tgz#2d284ea2ea1023fb3cdaac9aa61a86322d6801ff"
|
||||
integrity sha512-sC/q64XUDZWoxyAkzpxaw5u2BFNDiPp9K4PybTJFRW1e3VQwul2/iXcUvMf1RzQv4AkQWO8uLeHHG0wRGfCPcA==
|
||||
|
||||
"@testing-library/dom@^7.16.2":
|
||||
version "7.24.5"
|
||||
|
|
|
|||
Loading…
Reference in New Issue