Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0e18e8e966
commit
de828d08e2
|
|
@ -224,7 +224,6 @@
|
|||
- ".gitlab/ci/**/*.yml"
|
||||
- "lib/gitlab/ci/templates/**/*.yml"
|
||||
- "data/deprecations/**/*.yml"
|
||||
- "data/removals/**/*.yml"
|
||||
- "data/whats_new/**/*.yml"
|
||||
|
||||
.lint-metrics-yaml-patterns: &lint-metrics-yaml-patterns
|
||||
|
|
@ -256,22 +255,12 @@
|
|||
- '{yarn.lock,*/yarn.lock,*/*/yarn.lock}'
|
||||
|
||||
.python-patterns: &python-patterns
|
||||
- '{requirements.txt,*/requirements.txt,*/*/requirements.txt}'
|
||||
- '{requirements.pip,*/requirements.pip,*/*/requirements.pip}'
|
||||
- '{Pipfile,*/Pipfile,*/*/Pipfile}'
|
||||
- '{requires.txt,*/requires.txt,*/*/requires.txt}'
|
||||
- '{setup.py,*/setup.py,*/*/setup.py}'
|
||||
|
||||
.dependency-patterns: &dependency-patterns
|
||||
- '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}'
|
||||
- '{composer.lock,*/composer.lock,*/*/composer.lock}'
|
||||
- '{gems.locked,*/gems.locked,*/*/gems.locked}'
|
||||
- '{go.sum,*/go.sum,*/*/go.sum}'
|
||||
- '{npm-shrinkwrap.json,*/npm-shrinkwrap.json,*/*/npm-shrinkwrap.json}'
|
||||
- '{package-lock.json,*/package-lock.json,*/*/package-lock.json}'
|
||||
- '{yarn.lock,*/yarn.lock,*/*/yarn.lock}'
|
||||
- '{packages.lock.json,*/packages.lock.json,*/*/packages.lock.json}'
|
||||
- '{conan.lock,*/conan.lock,*/*/conan.lock}'
|
||||
|
||||
.frontend-dependency-patterns: &frontend-dependency-patterns
|
||||
- "{package.json,yarn.lock}"
|
||||
|
|
@ -344,7 +333,7 @@
|
|||
- "config.ru"
|
||||
# List explicitly all the app/ dirs that are backend (i.e. all except app/assets).
|
||||
- "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
|
||||
- "{,ee/,jh/}{bin,config,db,gems,generator_templates,lib}/**/*"
|
||||
- "{,ee/,jh/}{bin,config,db,elastic,gems,generator_templates,lib}/**/*"
|
||||
- "{,ee/,jh/}spec/**/*"
|
||||
# CI changes
|
||||
- ".gitlab-ci.yml"
|
||||
|
|
@ -360,7 +349,7 @@
|
|||
- "GITLAB_ELASTICSEARCH_INDEXER_VERSION"
|
||||
# List explicitly all the app/ dirs that are backend (i.e. all except app/assets).
|
||||
- "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
|
||||
- "{,ee/,jh/}{bin,config,db,generator_templates,lib}/**/*"
|
||||
- "{,ee/,jh/}{bin,config,db,elastic,gems,generator_templates,lib}/**/*"
|
||||
- "{,ee/,jh/}spec/**/*"
|
||||
|
||||
# Redis patterns + feature flags
|
||||
|
|
@ -370,7 +359,6 @@
|
|||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/usage_data_counters/{hll_redis_counter,redis_counter}{,_spec}.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/usage/metrics/instrumentations/redis{_metric,hll_metric}{,_spec}.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/usage/metrics/aggregates/sources/redis_hll{,_spec}.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/patch/action_cable_redis_listener{,_spec}.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/merge_requests/mergeability/redis_interface{,_spec}.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/markdown_cache/redis/*.rb"
|
||||
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/redis/**/*.rb"
|
||||
|
|
@ -383,9 +371,9 @@
|
|||
|
||||
# AI patterns:
|
||||
.ai-patterns: &ai-patterns
|
||||
- "{,ee/,jh/}lib/gitlab/llm/**/*"
|
||||
- "{,ee/,jh/}{,spec/}lib/gitlab/llm/**/*"
|
||||
- "{,ee/,jh/}lib/gitlab/duo/**/*"
|
||||
- "{ee/,jh/}lib/gitlab/llm/**/*"
|
||||
- "{ee/,jh/}{,spec/}lib/gitlab/llm/**/*"
|
||||
- "{ee/,jh/}lib/gitlab/duo/**/*"
|
||||
|
||||
# DB patterns + .ci-patterns
|
||||
.db-patterns: &db-patterns
|
||||
|
|
@ -453,8 +441,10 @@
|
|||
- "Rakefile"
|
||||
- "tests.yml"
|
||||
- "config.ru"
|
||||
- "{,ee/,jh/}{app,bin,config,db,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
|
||||
- "{,ee/,jh/}{app,bin,config,db,elastic,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
# Auto-generated files
|
||||
- "doc/api/graphql/reference/*"
|
||||
- "doc/administration/audit_event_streaming/audit_event_types.md"
|
||||
# CI changes
|
||||
- ".gitlab-ci.yml"
|
||||
- ".gitlab/ci/**/*"
|
||||
|
|
@ -477,8 +467,10 @@
|
|||
- "Rakefile"
|
||||
- "tests.yml"
|
||||
- "config.ru"
|
||||
- "{,ee/,jh/}{app,bin,config,db,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
|
||||
- "{,ee/,jh/}{app,bin,config,db,elastic,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
# Auto-generated files
|
||||
- "doc/api/graphql/reference/*"
|
||||
- "doc/administration/audit_event_streaming/audit_event_types.md"
|
||||
# CI changes
|
||||
- ".gitlab-ci.yml"
|
||||
- ".gitlab/ci/**/*"
|
||||
|
|
@ -508,8 +500,10 @@
|
|||
- "Rakefile"
|
||||
- "tests.yml"
|
||||
- "config.ru"
|
||||
- "{,ee/,jh/}{app,bin,config,db,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
|
||||
- "{,ee/,jh/}{app,bin,config,db,elastic,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
# Auto-generated files
|
||||
- "doc/api/graphql/reference/*"
|
||||
- "doc/administration/audit_event_streaming/audit_event_types.md"
|
||||
# CI changes
|
||||
- ".gitlab-ci.yml"
|
||||
- ".gitlab/ci/**/*"
|
||||
|
|
@ -536,7 +530,7 @@
|
|||
- "Rakefile"
|
||||
- "tests.yml"
|
||||
- "config.ru"
|
||||
- "{,ee/,jh/}{app,bin,config,db,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
- "{,ee/,jh/}{app,bin,config,db,elastic,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
# Auto-generated files
|
||||
- "doc/api/graphql/reference/*"
|
||||
- "doc/administration/audit_event_streaming/audit_event_types.md"
|
||||
|
|
@ -575,8 +569,10 @@
|
|||
- "Rakefile"
|
||||
- "tests.yml"
|
||||
- "config.ru"
|
||||
- "{,ee/,jh/}{app,bin,config,db,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
|
||||
- "{,ee/,jh/}{app,bin,config,db,elastic,generator_templates,gems,haml_lint,lib,locale,public,scripts,storybook,symbol,vendor}/**/*"
|
||||
# Auto-generated files
|
||||
- "doc/api/graphql/reference/*"
|
||||
- "doc/administration/audit_event_streaming/audit_event_types.md"
|
||||
# CI changes
|
||||
- ".gitlab-ci.yml"
|
||||
- ".gitlab/ci/**/*"
|
||||
|
|
@ -926,7 +922,7 @@
|
|||
variables:
|
||||
ARCH: amd64,arm64
|
||||
- <<: *if-ruby-branch
|
||||
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns", rules]
|
||||
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env", rules]
|
||||
|
||||
.build-images:rules:build-qa-image-as-if-foss:
|
||||
rules:
|
||||
|
|
@ -2501,7 +2497,7 @@
|
|||
when: never
|
||||
- if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab" && $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable-ee$/'
|
||||
|
||||
.releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns:
|
||||
.releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env:
|
||||
rules:
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /\[merge-train skip\]/'
|
||||
when: never
|
||||
|
|
@ -3237,7 +3233,7 @@
|
|||
when: never
|
||||
- <<: *if-merge-request-labels-pipeline-expedite
|
||||
when: never
|
||||
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns", rules]
|
||||
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env", rules]
|
||||
|
||||
###################
|
||||
# Benchmark rules #
|
||||
|
|
|
|||
|
|
@ -1101,7 +1101,6 @@ RSpec/FeatureCategory:
|
|||
- 'ee/spec/models/ee/project_member_spec.rb'
|
||||
- 'ee/spec/models/project_repository_state_spec.rb'
|
||||
- 'ee/spec/models/project_security_setting_spec.rb'
|
||||
- 'ee/spec/models/protected_branch/required_code_owners_section_spec.rb'
|
||||
- 'ee/spec/models/protected_environment_spec.rb'
|
||||
- 'ee/spec/models/protected_environments/approval_rule_spec.rb'
|
||||
- 'ee/spec/models/protected_environments/deploy_access_level_spec.rb'
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ export default {
|
|||
>
|
||||
{{ __('Show latest version') }}
|
||||
</gl-button>
|
||||
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
|
||||
<div v-if="hasChanges" class="inline-parallel-buttons d-none gl-md-display-flex! ml-auto">
|
||||
<diff-stats
|
||||
:diff-files-count-text="diffFilesCountText"
|
||||
:added-lines="addedLines"
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
"MergeRequest"
|
||||
],
|
||||
"OrchestrationPolicy": [
|
||||
"ApprovalPolicy",
|
||||
"ScanExecutionPolicy",
|
||||
"ScanResultPolicy"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export default {
|
|||
return {
|
||||
'gl-display-none': true,
|
||||
'gl-display-flex': this.tagCount === 1,
|
||||
'd-md-flex': this.tagCount > 1,
|
||||
'gl-md-display-flex!': this.tagCount > 1,
|
||||
'gl-mr-2': index !== this.tagsToRender.length - 1,
|
||||
'gl-ml-3': !this.hideLabel && index === 0,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
export const viewers = {
|
||||
csv: () => import('./csv_viewer.vue'),
|
||||
download: () => import('./download_viewer.vue'),
|
||||
download: () => import('jh_else_ce/repository/components/blob_viewers/download_viewer.vue'),
|
||||
image: () => import('./image_viewer.vue'),
|
||||
video: () => import('./video_viewer.vue'),
|
||||
empty: () => import('./empty_viewer.vue'),
|
||||
text: () => import('~/vue_shared/components/source_viewer/source_viewer.vue'),
|
||||
pdf: () => import('./pdf_viewer.vue'),
|
||||
lfs: () => import('./lfs_viewer.vue'),
|
||||
pdf: () => import('jh_else_ce/repository/components/blob_viewers/pdf_viewer.vue'),
|
||||
lfs: () => import('jh_else_ce/repository/components/blob_viewers/lfs_viewer.vue'),
|
||||
audio: () => import('./audio_viewer.vue'),
|
||||
svg: () => import('./image_viewer.vue'),
|
||||
sketch: () => import('./sketch_viewer.vue'),
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ export default {
|
|||
<div>
|
||||
<div class="gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<!-- hide header when editing, since we then have a form label. Keep it reachable for screenreader nav -->
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-scale-5">
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-5">
|
||||
{{ dropdownLabel }}
|
||||
</h3>
|
||||
<gl-loading-icon v-if="updateInProgress" />
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ export default {
|
|||
<template>
|
||||
<section class="gl-pb-4">
|
||||
<div class="gl-display-flex gl-align-items-center gl-gap-3">
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-scale-5">
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-5">
|
||||
{{ $options.i18n.dates }}
|
||||
</h3>
|
||||
<gl-button
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ export default {
|
|||
<div>
|
||||
<div class="gl-display-flex gl-align-items-center">
|
||||
<!-- hide header when editing, since we then have a form label. Keep it reachable for screenreader nav -->
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-scale-5">
|
||||
<h3 :class="{ 'gl-sr-only': isEditing }" class="gl-mb-0! gl-heading-5">
|
||||
{{ __('Parent') }}
|
||||
</h3>
|
||||
<gl-loading-icon
|
||||
|
|
|
|||
|
|
@ -69,9 +69,13 @@ class Projects::ForksController < Projects::ApplicationController
|
|||
@forked_project = fork_namespace.projects.find_by(path: project.path) # rubocop: disable CodeReuse/ActiveRecord
|
||||
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
|
||||
|
||||
@forked_project ||= fork_service.execute
|
||||
unless @forked_project
|
||||
@fork_response = fork_service.execute
|
||||
|
||||
if !@forked_project.saved? || !@forked_project.forked?
|
||||
@forked_project ||= @fork_response[:project] if @fork_response.success?
|
||||
end
|
||||
|
||||
if defined?(@fork_response) && @fork_response.error?
|
||||
render :error
|
||||
elsif @forked_project.import_in_progress?
|
||||
redirect_to project_import_path(@forked_project, continue: continue_params)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ module StatAnchorsHelper
|
|||
elsif anchor.is_link
|
||||
'stat-link'
|
||||
else
|
||||
"gl-button btn #{button_attribute(anchor)}"
|
||||
button_attribute(anchor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,14 +3,11 @@
|
|||
module Projects
|
||||
class ForkService < BaseService
|
||||
def execute(fork_to_project = nil)
|
||||
forked_project = fork_to_project ? link_existing_project(fork_to_project) : fork_new_project
|
||||
response = fork_to_project ? link_existing_project(fork_to_project) : fork_new_project
|
||||
|
||||
if forked_project&.saved?
|
||||
refresh_forks_count
|
||||
stream_audit_event(forked_project)
|
||||
end
|
||||
after_fork(response[:project]) if response.success?
|
||||
|
||||
forked_project
|
||||
response
|
||||
end
|
||||
|
||||
def valid_fork_targets(options = {})
|
||||
|
|
@ -29,23 +26,39 @@ module Projects
|
|||
|
||||
private
|
||||
|
||||
def after_fork(project)
|
||||
return unless project&.saved?
|
||||
|
||||
refresh_forks_count
|
||||
stream_audit_event(project)
|
||||
end
|
||||
|
||||
def link_existing_project(fork_to_project)
|
||||
return if fork_to_project.forked?
|
||||
if fork_to_project.forked?
|
||||
return ServiceResponse.error(message: _('Project already forked'), reason: :already_forked)
|
||||
end
|
||||
|
||||
build_fork_network_member(fork_to_project)
|
||||
|
||||
fork_to_project if link_fork_network(fork_to_project)
|
||||
if link_fork_network(fork_to_project)
|
||||
ServiceResponse.success(payload: { project: fork_to_project })
|
||||
else
|
||||
ServiceResponse.error(message: fork_to_project.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
def fork_new_project
|
||||
new_project = CreateService.new(current_user, new_fork_params).execute
|
||||
return new_project unless new_project.persisted?
|
||||
|
||||
unless new_project.persisted?
|
||||
return ServiceResponse.error(message: new_project.errors.full_messages)
|
||||
end
|
||||
|
||||
new_project.project_feature.update!(
|
||||
@project.project_feature.slice(ProjectFeature::FEATURES.map { |f| "#{f}_access_level" })
|
||||
)
|
||||
|
||||
new_project
|
||||
ServiceResponse.success(payload: { project: new_project })
|
||||
end
|
||||
|
||||
def new_fork_params
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
- anchors = local_assigns.fetch(:anchors, [])
|
||||
- project_buttons = local_assigns.fetch(:project_buttons, false)
|
||||
- ff_reorg_enabled = Feature.enabled?(:project_overview_reorg)
|
||||
- stat_text_classes = "stat-text d-flex gl-align-items-center #{'gl-px-0! gl-pb-2!' if ff_reorg_enabled}"
|
||||
|
||||
- return unless anchors.any?
|
||||
|
||||
%ul.nav.gl-row-gap-2.gl-column-gap-5
|
||||
- anchors.each do |anchor|
|
||||
%li.nav-item
|
||||
= link_to_if(anchor.link, anchor.label, anchor.link, stat_anchor_attrs(anchor)) do
|
||||
.stat-text.d-flex.gl-align-items-center{ class: "#{'btn gl-button btn-default disabled' if project_buttons} #{'gl-px-0! gl-pb-2!' if ff_reorg_enabled}" }= anchor.label
|
||||
- if anchor.link # render actionable link/button
|
||||
- if anchor.is_link || ff_reorg_enabled
|
||||
= link_to(anchor.label, anchor.link, stat_anchor_attrs(anchor))
|
||||
- else
|
||||
= render Pajamas::ButtonComponent.new(href: anchor.link, button_options: stat_anchor_attrs(anchor)) do
|
||||
= anchor.label
|
||||
- elsif project_buttons # render disabled button
|
||||
= render Pajamas::ButtonComponent.new(disabled: true, button_options: { classes: stat_text_classes }) do
|
||||
= anchor.label
|
||||
- else # render as text label
|
||||
%div{ class: stat_text_classes }= anchor.label
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
- page_title _("Fork project")
|
||||
- if @forked_project && !@forked_project.saved?
|
||||
- if @fork_response.error?
|
||||
= render Pajamas::AlertComponent.new(title: _('Fork Error!'),
|
||||
variant: :danger,
|
||||
alert_options: { class: 'gl-mt-5' },
|
||||
|
|
@ -8,14 +8,10 @@
|
|||
%p
|
||||
= _("You tried to fork %{link_to_the_project} but it failed for the following reason:").html_safe % { link_to_the_project: link_to_project(@project) }
|
||||
|
||||
- if @forked_project && @forked_project.errors.any?
|
||||
- @fork_response.errors.each do |error|
|
||||
%p
|
||||
–
|
||||
- error = @forked_project.errors.full_messages.first
|
||||
- if error.include?("already been taken")
|
||||
= _('Name has already been taken')
|
||||
- else
|
||||
= error
|
||||
= error
|
||||
|
||||
- c.with_actions do
|
||||
= link_button_to _('Try to fork again'), new_project_fork_path(@project), title: _("Fork"), class: 'gl-alert-action', variant: :confirm
|
||||
|
|
|
|||
|
|
@ -7,4 +7,19 @@ feature_categories:
|
|||
description: Stores verification state for Geo replicated Pages deployments.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74905
|
||||
milestone: '14.6'
|
||||
gitlab_schema: gitlab_main
|
||||
gitlab_schema: gitlab_main_cell
|
||||
allow_cross_joins:
|
||||
- gitlab_main_clusterwide
|
||||
allow_cross_transactions:
|
||||
- gitlab_main_clusterwide
|
||||
allow_cross_foreign_keys:
|
||||
- gitlab_main_clusterwide
|
||||
desired_sharding_key:
|
||||
project_id:
|
||||
references: projects
|
||||
backfill_via:
|
||||
parent:
|
||||
foreign_key: pages_deployment_id
|
||||
table: pages_deployments
|
||||
sharding_key: project_id
|
||||
belongs_to: pages_deployment
|
||||
|
|
|
|||
|
|
@ -128,7 +128,15 @@ class Gitlab::Seeder::Projects
|
|||
project = Project.find_by_full_path(project_name)
|
||||
|
||||
User.offset(1).first(5).each do |user|
|
||||
new_project = ::Projects::ForkService.new(project, user).execute
|
||||
response = ::Projects::ForkService.new(project, user).execute
|
||||
|
||||
if response.error?
|
||||
print 'F'
|
||||
puts response.errors
|
||||
next
|
||||
end
|
||||
|
||||
new_project = response[:project]
|
||||
|
||||
if new_project.valid? && (new_project.valid_repo? || new_project.import_state.scheduled?)
|
||||
print '.'
|
||||
|
|
|
|||
|
|
@ -11,13 +11,20 @@ Sidekiq::Testing.inline! do
|
|||
next unless source_project
|
||||
|
||||
Sidekiq::Worker.skipping_transaction_check do
|
||||
fork_project = Projects::ForkService.new(
|
||||
response = Projects::ForkService.new(
|
||||
source_project,
|
||||
user,
|
||||
namespace: user.namespace,
|
||||
skip_disk_validation: true
|
||||
).execute
|
||||
|
||||
if response.error?
|
||||
print 'F'
|
||||
next
|
||||
end
|
||||
|
||||
fork_project = response[:project]
|
||||
|
||||
# Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
|
||||
# hook won't run until after the fixture is loaded. That is too late
|
||||
# since the Sidekiq::Testing block has already exited. Force clearing
|
||||
|
|
|
|||
|
|
@ -9085,6 +9085,29 @@ The edge type for [`AmazonS3ConfigurationType`](#amazons3configurationtype).
|
|||
| <a id="amazons3configurationtypeedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="amazons3configurationtypeedgenode"></a>`node` | [`AmazonS3ConfigurationType`](#amazons3configurationtype) | The item at the end of the edge. |
|
||||
|
||||
#### `ApprovalPolicyConnection`
|
||||
|
||||
The connection type for [`ApprovalPolicy`](#approvalpolicy).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="approvalpolicyconnectionedges"></a>`edges` | [`[ApprovalPolicyEdge]`](#approvalpolicyedge) | A list of edges. |
|
||||
| <a id="approvalpolicyconnectionnodes"></a>`nodes` | [`[ApprovalPolicy]`](#approvalpolicy) | A list of nodes. |
|
||||
| <a id="approvalpolicyconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `ApprovalPolicyEdge`
|
||||
|
||||
The edge type for [`ApprovalPolicy`](#approvalpolicy).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="approvalpolicyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="approvalpolicyedgenode"></a>`node` | [`ApprovalPolicy`](#approvalpolicy) | The item at the end of the edge. |
|
||||
|
||||
#### `ApprovalProjectRuleConnection`
|
||||
|
||||
The connection type for [`ApprovalProjectRule`](#approvalprojectrule).
|
||||
|
|
@ -14747,6 +14770,26 @@ An API Fuzzing scan profile.
|
|||
| <a id="apifuzzingscanprofilename"></a>`name` | [`String`](#string) | Unique name of the profile. |
|
||||
| <a id="apifuzzingscanprofileyaml"></a>`yaml` | [`String`](#string) | Syntax highlighted HTML representation of the YAML. |
|
||||
|
||||
### `ApprovalPolicy`
|
||||
|
||||
Represents the approval policy.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="approvalpolicyallgroupapprovers"></a>`allGroupApprovers` | [`[PolicyApprovalGroup!]`](#policyapprovalgroup) | All potential approvers of the group type, including groups inaccessible to the user. |
|
||||
| <a id="approvalpolicydescription"></a>`description` | [`String!`](#string) | Description of the policy. |
|
||||
| <a id="approvalpolicyeditpath"></a>`editPath` | [`String!`](#string) | URL of policy edit page. |
|
||||
| <a id="approvalpolicyenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether this policy is enabled. |
|
||||
| <a id="approvalpolicygroupapprovers"></a>`groupApprovers` **{warning-solid}** | [`[Group!]`](#group) | **Deprecated** in 16.5. Use `allGroupApprovers`. |
|
||||
| <a id="approvalpolicyname"></a>`name` | [`String!`](#string) | Name of the policy. |
|
||||
| <a id="approvalpolicyroleapprovers"></a>`roleApprovers` | [`[MemberAccessLevelName!]`](#memberaccesslevelname) | Approvers of the role type. Users belonging to these role(s) alone will be approvers. |
|
||||
| <a id="approvalpolicysource"></a>`source` | [`SecurityPolicySource!`](#securitypolicysource) | Source of the policy. Its fields depend on the source type. |
|
||||
| <a id="approvalpolicyupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the policy YAML was last updated. |
|
||||
| <a id="approvalpolicyuserapprovers"></a>`userApprovers` | [`[UserCore!]`](#usercore) | Approvers of the user type. |
|
||||
| <a id="approvalpolicyyaml"></a>`yaml` | [`String!`](#string) | YAML definition of the policy. |
|
||||
|
||||
### `ApprovalProjectRule`
|
||||
|
||||
Describes a project approval rule regarding who can approve merge requests.
|
||||
|
|
@ -19581,6 +19624,22 @@ Returns [`AddOnPurchase`](#addonpurchase).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="groupaddonpurchaseaddonname"></a>`addOnName` | [`String!`](#string) | AddOn name. |
|
||||
|
||||
##### `Group.approvalPolicies`
|
||||
|
||||
Approval Policies of the project.
|
||||
|
||||
Returns [`ApprovalPolicyConnection`](#approvalpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#connection-pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupapprovalpoliciesrelationship"></a>`relationship` | [`SecurityPolicyRelationType`](#securitypolicyrelationtype) | Filter policies by the given policy relationship. |
|
||||
|
||||
##### `Group.autocompleteUsers`
|
||||
|
||||
Search users for autocompletion.
|
||||
|
|
@ -20400,6 +20459,10 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
Scan Result Policies of the project.
|
||||
|
||||
NOTE:
|
||||
**Deprecated** in 16.9.
|
||||
Use `approvalPolicies`.
|
||||
|
||||
Returns [`ScanResultPolicyConnection`](#scanresultpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
|
|
@ -23369,6 +23432,22 @@ Returns [`AddOnPurchase`](#addonpurchase).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="namespaceaddonpurchaseaddonname"></a>`addOnName` | [`String!`](#string) | AddOn name. |
|
||||
|
||||
##### `Namespace.approvalPolicies`
|
||||
|
||||
Approval Policies of the project.
|
||||
|
||||
Returns [`ApprovalPolicyConnection`](#approvalpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#connection-pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="namespaceapprovalpoliciesrelationship"></a>`relationship` | [`SecurityPolicyRelationType`](#securitypolicyrelationtype) | Filter policies by the given policy relationship. |
|
||||
|
||||
##### `Namespace.complianceFrameworks`
|
||||
|
||||
Compliance frameworks available to projects in this namespace.
|
||||
|
|
@ -23435,6 +23514,10 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
Scan Result Policies of the project.
|
||||
|
||||
NOTE:
|
||||
**Deprecated** in 16.9.
|
||||
Use `approvalPolicies`.
|
||||
|
||||
Returns [`ScanResultPolicyConnection`](#scanresultpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
|
|
@ -24649,6 +24732,22 @@ Returns [`[AlertManagementPayloadAlertField!]`](#alertmanagementpayloadalertfiel
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="projectalertmanagementpayloadfieldspayloadexample"></a>`payloadExample` | [`String!`](#string) | Sample payload for extracting alert fields for custom mappings. |
|
||||
|
||||
##### `Project.approvalPolicies`
|
||||
|
||||
Approval Policies of the project.
|
||||
|
||||
Returns [`ApprovalPolicyConnection`](#approvalpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
four standard [pagination arguments](#connection-pagination-arguments):
|
||||
`before: String`, `after: String`, `first: Int`, and `last: Int`.
|
||||
|
||||
###### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="projectapprovalpoliciesrelationship"></a>`relationship` | [`SecurityPolicyRelationType`](#securitypolicyrelationtype) | Filter policies by the given policy relationship. |
|
||||
|
||||
##### `Project.autocompleteUsers`
|
||||
|
||||
Search users for autocompletion.
|
||||
|
|
@ -25769,6 +25868,10 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
Scan Result Policies of the project.
|
||||
|
||||
NOTE:
|
||||
**Deprecated** in 16.9.
|
||||
Use `approvalPolicies`.
|
||||
|
||||
Returns [`ScanResultPolicyConnection`](#scanresultpolicyconnection).
|
||||
|
||||
This field returns a [connection](#connections). It accepts the
|
||||
|
|
@ -33749,6 +33852,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
Implementations:
|
||||
|
||||
- [`ApprovalPolicy`](#approvalpolicy)
|
||||
- [`ScanExecutionPolicy`](#scanexecutionpolicy)
|
||||
- [`ScanResultPolicy`](#scanresultpolicy)
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ A personal access token can perform actions based on the assigned scopes.
|
|||
|
||||
| Scope | Access |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `api` | Grants complete read/write access to the API, including all groups and projects, the container registry, the dependency proxy, and the package registry. |
|
||||
| `api` | Grants complete read/write access to the API, including all groups and projects, the container registry, the dependency proxy, and the package registry. Also grants complete read/write access to the registry and repository using Git over HTTP. |
|
||||
| `read_user` | Grants read-only access to the authenticated user's profile through the `/user` API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under [`/users`](../../api/users.md). |
|
||||
| `read_api` | Grants read access to the API, including all groups and projects, the container registry, and the package registry. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28944) in GitLab 12.10.) |
|
||||
| `read_repository` | Grants read-only access to repositories on private projects using Git-over-HTTP or the Repository Files API. |
|
||||
|
|
|
|||
|
|
@ -495,16 +495,16 @@ module API
|
|||
not_found!('Source Branch') if fork_params[:branches].present? && !service.valid_fork_branch?(fork_params[:branches])
|
||||
not_found!('Target Namespace') unless service.valid_fork_target?
|
||||
|
||||
forked_project = service.execute
|
||||
result = service.execute
|
||||
|
||||
if forked_project.errors.any?
|
||||
conflict!(forked_project.errors.messages)
|
||||
else
|
||||
present_project forked_project, {
|
||||
if result.success?
|
||||
present_project result[:project], {
|
||||
with: Entities::Project,
|
||||
user_can_admin_project: can?(current_user, :admin_project, forked_project),
|
||||
user_can_admin_project: can?(current_user, :admin_project, result[:project]),
|
||||
current_user: current_user
|
||||
}
|
||||
else
|
||||
conflict!(result.message)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -719,10 +719,12 @@ module API
|
|||
|
||||
result = service.execute(user_project)
|
||||
|
||||
if result
|
||||
if result.success?
|
||||
present_project user_project.reset, with: Entities::Project, current_user: current_user
|
||||
elsif user_project.forked?
|
||||
render_api_error!("Project already forked", 409)
|
||||
elsif result.reason == :already_forked
|
||||
conflict!(result.message)
|
||||
else
|
||||
render_api_error!(result.message, 400)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -31847,9 +31847,6 @@ msgstr ""
|
|||
msgid "Name can't be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "Name has already been taken"
|
||||
msgstr ""
|
||||
|
||||
msgid "Name is already taken."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -38275,6 +38272,9 @@ msgstr ""
|
|||
msgid "Project already deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project already forked"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project and wiki repositories"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ gem 'capybara', '~> 3.39.2'
|
|||
gem 'capybara-screenshot', '~> 1.0.26'
|
||||
gem 'rake', '~> 13', '>= 13.1.0'
|
||||
gem 'rspec', '~> 3.12'
|
||||
gem 'selenium-webdriver', '= 4.16.0'
|
||||
gem 'selenium-webdriver', '= 4.17.0'
|
||||
gem 'airborne', '~> 0.3.7', require: false # airborne is messing with rspec sandboxed mode so not requiring by default
|
||||
gem 'rest-client', '~> 2.1.0'
|
||||
gem 'rspec-retry', '~> 0.6.2', require: 'rspec/retry'
|
||||
|
|
@ -39,7 +39,7 @@ gem 'chemlab-library-www-gitlab-com', '~> 0.1', '>= 0.1.1'
|
|||
# dependencies for jenkins client
|
||||
gem 'nokogiri', '~> 1.16'
|
||||
|
||||
gem 'deprecation_toolkit', '~> 2.1.0', require: false
|
||||
gem 'deprecation_toolkit', '~> 2.2.0', require: false
|
||||
|
||||
gem 'factory_bot', '~> 6.3.0'
|
||||
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ GEM
|
|||
crass (1.0.6)
|
||||
debug_inspector (1.1.0)
|
||||
declarative (0.0.20)
|
||||
deprecation_toolkit (2.1.0)
|
||||
activesupport (>= 7.0)
|
||||
deprecation_toolkit (2.2.0)
|
||||
activesupport (>= 6.1)
|
||||
diff-lcs (1.3)
|
||||
domain_name (0.6.20240107)
|
||||
erubi (1.12.0)
|
||||
|
|
@ -301,7 +301,8 @@ GEM
|
|||
sawyer (0.9.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (>= 0.17.3, < 3)
|
||||
selenium-webdriver (4.16.0)
|
||||
selenium-webdriver (4.17.0)
|
||||
base64 (~> 0.2)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
|
|
@ -350,7 +351,7 @@ DEPENDENCIES
|
|||
capybara-screenshot (~> 1.0.26)
|
||||
chemlab (~> 0.11, >= 0.11.1)
|
||||
chemlab-library-www-gitlab-com (~> 0.1, >= 0.1.1)
|
||||
deprecation_toolkit (~> 2.1.0)
|
||||
deprecation_toolkit (~> 2.2.0)
|
||||
factory_bot (~> 6.3.0)
|
||||
faker (~> 3.2, >= 3.2.3)
|
||||
faraday-retry (~> 2.2)
|
||||
|
|
@ -375,7 +376,7 @@ DEPENDENCIES
|
|||
rspec-retry (~> 0.6.2)
|
||||
rspec_junit_formatter (~> 0.6.0)
|
||||
ruby-debug-ide (~> 0.7.3)
|
||||
selenium-webdriver (= 4.16.0)
|
||||
selenium-webdriver (= 4.17.0)
|
||||
slack-notifier (~> 2.4)
|
||||
terminal-table (~> 3.0.2)
|
||||
warning (~> 1.3)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ module QA
|
|||
|
||||
it(
|
||||
'after a push via the API creates a merge request',
|
||||
quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/403182',
|
||||
type: :flaky
|
||||
},
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/360490'
|
||||
) do
|
||||
commit = create(:commit,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ module QA
|
|||
end
|
||||
|
||||
context 'when developers and maintainers are not allowed to push to a protected branch' do
|
||||
it 'user without push rights fails to push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347757' do
|
||||
it 'user without push rights fails to push', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347757',
|
||||
quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/426739',
|
||||
type: :flaky
|
||||
} do
|
||||
create_protected_branch(allowed_to_push: {
|
||||
roles: Resource::ProtectedBranch::Roles::NO_ONE
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@ module QA
|
|||
Runtime::Feature.disable(:rubygem_packages, project: project)
|
||||
end
|
||||
|
||||
it 'publishes a Ruby gem', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347649' do
|
||||
it 'publishes a Ruby gem', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347649',
|
||||
quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/366099',
|
||||
type: :flaky
|
||||
} do
|
||||
Flow::Login.sign_in
|
||||
|
||||
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Projects::ForksController, feature_category: :source_code_management do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
let(:forked_project) { Projects::ForkService.new(project, user, name: 'Some name').execute }
|
||||
let(:forked_project) { Projects::ForkService.new(project, user, name: 'Some name').execute[:project] }
|
||||
let(:group) { create(:group) }
|
||||
|
||||
before do
|
||||
|
|
@ -271,6 +271,34 @@ RSpec.describe Projects::ForksController, feature_category: :source_code_managem
|
|||
end
|
||||
end
|
||||
|
||||
context 'when fork already exists' do
|
||||
before do
|
||||
forked_project
|
||||
end
|
||||
|
||||
it 'responds with status 302' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to redirect_to(namespace_project_import_path(user.namespace, project))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when fork process fails' do
|
||||
before do
|
||||
allow_next_instance_of(Projects::ForkService) do |instance|
|
||||
allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: 'Error'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'responds with an error page' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to render_template(:error)
|
||||
end
|
||||
end
|
||||
|
||||
context 'continue params' do
|
||||
let(:params) do
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
require 'fast_spec_helper'
|
||||
|
||||
PatternsList = Struct.new(:name, :patterns)
|
||||
|
||||
RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
|
||||
config = YAML.safe_load_file(
|
||||
File.expand_path('../../.gitlab/ci/rules.gitlab-ci.yml', __dir__),
|
||||
|
|
@ -58,4 +60,130 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'patterns' do
|
||||
foss_context = !Gitlab.ee?
|
||||
no_matching_needed_files = (
|
||||
[
|
||||
'.byebug_history',
|
||||
'.editorconfig',
|
||||
'.eslintcache',
|
||||
'.foreman',
|
||||
'.git-blame-ignore-revs',
|
||||
'.gitlab_kas_secret',
|
||||
'.gitlab_shell_secret',
|
||||
'.gitlab_workhorse_secret',
|
||||
'.gitlab/agents/review-apps/config.yaml',
|
||||
'.gitlab/changelog_config.yml',
|
||||
'.gitlab/CODEOWNERS',
|
||||
'.gitleaksignore',
|
||||
'.gitpod.yml',
|
||||
'.license_encryption_key.pub',
|
||||
'.mailmap',
|
||||
'.prettierignore',
|
||||
'.projections.json.example',
|
||||
'.rubocop_revert_ignores.txt',
|
||||
'.ruby-version',
|
||||
'.solargraph.yml.example',
|
||||
'.solargraph.yml',
|
||||
'.test_license_encryption_key.pub',
|
||||
'.tool-versions',
|
||||
'.vale.ini',
|
||||
'.vscode/extensions.json',
|
||||
'ee/lib/ee/gitlab/background_migration/.rubocop.yml',
|
||||
'ee/LICENSE',
|
||||
'Gemfile.checksum',
|
||||
'gems/error_tracking_open_api/.openapi-generator/FILES',
|
||||
'gems/error_tracking_open_api/.openapi-generator/VERSION',
|
||||
'Guardfile',
|
||||
'INSTALLATION_TYPE',
|
||||
'lib/gitlab/background_migration/.rubocop.yml',
|
||||
'lib/gitlab/ci/templates/.yamllint',
|
||||
'LICENSE',
|
||||
'Pipfile.lock',
|
||||
'storybook/.env.template',
|
||||
'yarn-error.log'
|
||||
] +
|
||||
Dir.glob('.bundle/**/*') +
|
||||
Dir.glob('.github/*') +
|
||||
Dir.glob('.gitlab/{issue,merge_request}_templates/**/*') +
|
||||
Dir.glob('.gitlab/*.toml') +
|
||||
Dir.glob('{,**/}.{DS_Store,eslintrc.yml,gitignore,gitkeep,keep}', File::FNM_DOTMATCH) +
|
||||
Dir.glob('{,vendor/}gems/*/.*') +
|
||||
Dir.glob('{.git,.lefthook,.ruby-lsp}/**/*') +
|
||||
Dir.glob('{file_hooks,log}/**/*') +
|
||||
Dir.glob('{metrics_server,sidekiq_cluster}/*') +
|
||||
Dir.glob('{spec/fixtures,tmp}/**/*', File::FNM_DOTMATCH) +
|
||||
Dir.glob('*.md') +
|
||||
Dir.glob('changelogs/*') +
|
||||
Dir.glob('doc/.{markdownlint,vale}/**/*') +
|
||||
Dir.glob('keeps/**/*') +
|
||||
Dir.glob('node_modules/**/*', File::FNM_DOTMATCH) +
|
||||
Dir.glob('patches/*') +
|
||||
Dir.glob('public/assets/**/.*') +
|
||||
Dir.glob('qa/.{,**/}*') +
|
||||
Dir.glob('qa/**/.gitlab-ci.yml') +
|
||||
Dir.glob('shared/**/*') +
|
||||
Dir.glob('workhorse/.*')
|
||||
).freeze
|
||||
no_matching_needed_files_ci_specific = (
|
||||
[
|
||||
'metrics.txt'
|
||||
] +
|
||||
Dir.glob('{auto_explain,crystalball,knapsack,rspec}/**/*') +
|
||||
Dir.glob('coverage/**/*', File::FNM_DOTMATCH) +
|
||||
Dir.glob('vendor/ruby/**/*', File::FNM_DOTMATCH)
|
||||
).freeze
|
||||
all_files = Dir.glob('{,**/}*', File::FNM_DOTMATCH) -
|
||||
no_matching_needed_files -
|
||||
no_matching_needed_files_ci_specific
|
||||
all_files -= Dir.glob('ee/**/*', File::FNM_DOTMATCH) if foss_context
|
||||
all_files.reject! { |f| File.directory?(f) }
|
||||
|
||||
# One loop to construct an array of PatternsList objects
|
||||
patterns_lists = config.filter_map do |name, patterns|
|
||||
next unless name.start_with?('.')
|
||||
next unless name.end_with?('patterns')
|
||||
# Ignore EE-only patterns list when in FOSS context
|
||||
next if foss_context && patterns.all? { |pattern| pattern =~ %r|{?ee/| }
|
||||
|
||||
PatternsList.new(name, patterns)
|
||||
end
|
||||
|
||||
# One loop to gather a { pattern => files } hash
|
||||
patterns_files = patterns_lists.each_with_object({}) do |patterns_list, memo|
|
||||
patterns_list.patterns.each do |pattern|
|
||||
memo[pattern] ||= Dir.glob(pattern)
|
||||
end
|
||||
end
|
||||
|
||||
# Example: '.ci-patterns': [".gitlab-ci.yml", ".gitlab/ci/**/*", "scripts/rspec_helpers.sh"]
|
||||
patterns_lists.each do |patterns_list|
|
||||
describe "patterns list `#{patterns_list.name}`" do
|
||||
patterns_list.patterns.each do |pattern|
|
||||
pattern_files = patterns_files.fetch(pattern)
|
||||
|
||||
context "with `#{pattern}`" do
|
||||
it 'matches' do
|
||||
matching_files = (all_files & pattern_files)
|
||||
|
||||
expect(matching_files).not_to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'missed matched files' do
|
||||
all_matching_files = Set.new
|
||||
|
||||
patterns_files.each_value do |files|
|
||||
all_matching_files.merge(files)
|
||||
end
|
||||
|
||||
it 'does not miss files to match' do
|
||||
expect(all_files - all_matching_files.to_a).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ describe('PackageTags', () => {
|
|||
it('shows tag badge for medium or heigher resolutions', () => {
|
||||
createComponent(mockTags);
|
||||
|
||||
const expectedStyle = [...defaultStyle, 'd-md-flex'];
|
||||
const expectedStyle = [...defaultStyle, 'gl-md-display-flex!'];
|
||||
|
||||
expect(tagBadges().at(1).classes()).toEqual(expect.arrayContaining(expectedStyle));
|
||||
});
|
||||
|
|
@ -87,7 +87,7 @@ describe('PackageTags', () => {
|
|||
tagDisplayLimit: 4,
|
||||
});
|
||||
|
||||
const expectedStyleWithoutAppend = [...defaultStyle, 'd-md-flex'];
|
||||
const expectedStyleWithoutAppend = [...defaultStyle, 'gl-md-display-flex!'];
|
||||
const expectedStyleWithAppend = [...expectedStyleWithoutAppend, 'gl-mr-2'];
|
||||
|
||||
const allBadges = tagBadges();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ RSpec.describe StatAnchorsHelper do
|
|||
let(:anchor) { anchor_klass.new(false, nil, nil, 'btn-default') }
|
||||
|
||||
it 'returns the proper attributes' do
|
||||
expect(subject[:class]).to include('gl-button btn btn-default')
|
||||
expect(subject[:class]).to include('btn-default')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ RSpec.describe StatAnchorsHelper do
|
|||
let(:anchor) { anchor_klass.new(false) }
|
||||
|
||||
it 'returns the proper attributes' do
|
||||
expect(subject[:class]).to include('gl-button btn btn-dashed')
|
||||
expect(subject[:class]).to include('btn-dashed')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
|
|||
end
|
||||
|
||||
context "with a cross-project fork reference" do
|
||||
let(:forked_project) { Projects::ForkService.new(project, project2.creator).execute }
|
||||
let(:forked_project) { Projects::ForkService.new(project, project2.creator).execute[:project] }
|
||||
let(:fork_cross_reference) { issue.to_reference(forked_project) }
|
||||
|
||||
subject { described_class.new(forked_project, forked_project.creator) }
|
||||
|
|
|
|||
|
|
@ -3513,6 +3513,24 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
|
||||
expect(project_fork_target).to be_forked
|
||||
end
|
||||
|
||||
context 'when forking process fails' do
|
||||
before do
|
||||
allow_next_instance_of(Projects::ForkService) do |instance|
|
||||
allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: 'Error'))
|
||||
end
|
||||
end
|
||||
|
||||
it 'fails with 400 error' do
|
||||
expect(project_fork_target).not_to be_forked
|
||||
|
||||
post api(path, admin, admin_mode: true)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq "Error"
|
||||
expect(project_fork_target).not_to be_forked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -5039,8 +5057,13 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
post api("/projects/#{new_project.id}/fork", user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:conflict)
|
||||
expect(json_response['message']['name']).to eq(['has already been taken'])
|
||||
expect(json_response['message']['path']).to eq(['has already been taken'])
|
||||
expect(json_response['message']).to match_array(
|
||||
[
|
||||
'Name has already been taken',
|
||||
'Path has already been taken',
|
||||
'Project namespace name has already been taken'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'fails if project to fork from does not exist' do
|
||||
|
|
@ -5192,7 +5215,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
post api("/projects/#{project2.id}/fork", user2), params: { path: 'foobar' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:conflict)
|
||||
expect(json_response['message']['path']).to eq(['has already been taken'])
|
||||
expect(json_response['message']).to eq(['Path has already been taken'])
|
||||
end
|
||||
|
||||
it 'accepts custom parameters for the target project' do
|
||||
|
|
@ -5222,7 +5245,12 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
post api("/projects/#{project2.id}/fork", user2), params: { name: 'My Random Project' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:conflict)
|
||||
expect(json_response['message']['name']).to eq(['has already been taken'])
|
||||
expect(json_response['message']).to match_array(
|
||||
[
|
||||
'Name has already been taken',
|
||||
'Project namespace name has already been taken'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'forks to the same namespace with alternative path and name' do
|
||||
|
|
@ -5241,8 +5269,13 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
post api(path, user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:conflict)
|
||||
expect(json_response['message']['path']).to eq(['has already been taken'])
|
||||
expect(json_response['message']['name']).to eq(['has already been taken'])
|
||||
expect(json_response['message']).to match_array(
|
||||
[
|
||||
'Name has already been taken',
|
||||
'Path has already been taken',
|
||||
'Project namespace name has already been taken'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it 'fails to fork with an unknown visibility level' do
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ RSpec.describe UsersController, feature_category: :user_management do
|
|||
|
||||
context 'forked project' do
|
||||
let(:project) { create(:project) }
|
||||
let(:forked_project) { Projects::ForkService.new(project, user).execute }
|
||||
let(:forked_project) { Projects::ForkService.new(project, user).execute[:project] }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
describe '#execute' do
|
||||
subject(:fork_of_project) { service.execute }
|
||||
subject(:response) { service.execute }
|
||||
|
||||
let(:fork_of_project) { response[:project] }
|
||||
|
||||
before do
|
||||
# NOTE: Avatar file is dropped after project reload. Explicitly re-add it for each test.
|
||||
|
|
@ -37,8 +39,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'does not create a fork' do
|
||||
is_expected.not_to be_persisted
|
||||
expect(subject.errors[:forked_from_project_id]).to eq(['is forbidden'])
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to eq(['Forked from project is forbidden'])
|
||||
end
|
||||
|
||||
it 'does not create a fork network' do
|
||||
|
|
@ -52,7 +54,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'creates a fork of the project' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project.errors).to be_empty
|
||||
expect(fork_of_project.first_owner).to eq(user)
|
||||
expect(fork_of_project.namespace).to eq(user.namespace)
|
||||
|
|
@ -71,7 +73,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
# https://gitlab.com/gitlab-org/gitlab-foss/issues/26158
|
||||
it 'after forking the original project still has its avatar' do
|
||||
# If we do not fork the project first we cannot detect the bug.
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(project.avatar.file).to be_exists
|
||||
end
|
||||
|
|
@ -107,10 +109,16 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:other_namespace) { create(:group).tap { |group| group.add_owner(user) } }
|
||||
|
||||
it 'creates a new project' do
|
||||
fork_of_project = described_class.new(project, user, params).execute
|
||||
fork_response = described_class.new(project, user, params).execute
|
||||
expect(fork_response).to be_success
|
||||
|
||||
fork_of_project = fork_response[:project]
|
||||
expect(fork_of_project).to be_persisted
|
||||
|
||||
fork_of_fork = described_class.new(fork_of_project, user, { namespace: other_namespace }).execute
|
||||
fork_of_fork_response = described_class.new(fork_of_project, user, { namespace: other_namespace }).execute
|
||||
expect(fork_of_fork_response).to be_success
|
||||
|
||||
fork_of_fork = fork_of_fork_response[:project]
|
||||
expect(fork_of_fork).to be_persisted
|
||||
|
||||
expect(fork_of_fork).to be_valid
|
||||
|
|
@ -122,7 +130,10 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:root_project) { create(:project, :public) }
|
||||
|
||||
it 'successfully creates a fork of the fork with correct visibility' do
|
||||
fork_of_project = described_class.new(root_project, user, params).execute
|
||||
fork_response = described_class.new(root_project, user, params).execute
|
||||
expect(fork_response).to be_success
|
||||
|
||||
fork_of_project = fork_response[:project]
|
||||
expect(fork_of_project).to be_persisted
|
||||
|
||||
root_project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
|
|
@ -130,7 +141,10 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
# Forked project visibility is not affected by root project visibility change
|
||||
expect(fork_of_project).to have_attributes(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
|
||||
fork_of_fork = described_class.new(fork_of_project, user, { namespace: other_namespace }).execute
|
||||
fork_of_fork_response = described_class.new(fork_of_project, user, { namespace: other_namespace }).execute
|
||||
expect(fork_of_fork_response).to be_success
|
||||
|
||||
fork_of_fork = fork_of_fork_response[:project]
|
||||
expect(fork_of_fork).to be_persisted
|
||||
|
||||
expect(fork_of_fork).to be_valid
|
||||
|
|
@ -139,7 +153,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it_behaves_like 'forks count cache refresh' do
|
||||
let(:from_project) { described_class.new(project, user, { namespace: other_namespace }).execute }
|
||||
let(:from_project) { described_class.new(project, user, { namespace: other_namespace }).execute[:project] }
|
||||
let(:to_user) { user }
|
||||
end
|
||||
end
|
||||
|
|
@ -149,8 +163,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
existing_project = create(:project, namespace: namespace, path: project.path)
|
||||
expect(existing_project).to be_persisted
|
||||
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors[:path]).to eq(['has already been taken'])
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to include('Path has already been taken')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -167,17 +181,17 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'does not allow creation' do
|
||||
fork_of_project
|
||||
is_expected.to be_error
|
||||
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors.messages).to have_key(:base)
|
||||
expect(fork_of_project.errors.messages[:base].first).to match('There is already a repository with that name on disk')
|
||||
expect(response.errors).to include('There is already a repository with that name on disk')
|
||||
end
|
||||
|
||||
context 'when repository disk validation is explicitly skipped' do
|
||||
let(:params) { super().merge(skip_disk_validation: true) }
|
||||
|
||||
it 'allows fork project creation' do
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.errors.messages).to be_empty
|
||||
end
|
||||
|
|
@ -189,6 +203,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
it 'inherits default_git_depth from the origin project' do
|
||||
project.update!(ci_default_git_depth: 42)
|
||||
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.ci_default_git_depth).to eq(42)
|
||||
end
|
||||
|
|
@ -198,6 +213,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
it 'the fork has git depth set to 0' do
|
||||
project.update!(ci_default_git_depth: nil)
|
||||
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.ci_default_git_depth).to eq(0)
|
||||
end
|
||||
|
|
@ -212,6 +228,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'creates fork with lowest level' do
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
|
@ -223,8 +240,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it "doesn't create a fork" do
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors[:visibility_level]).to eq ['private has been restricted by your GitLab administrator']
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to eq ['Visibility level private has been restricted by your GitLab administrator']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -235,8 +252,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'does not create a fork' do
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors[:forked_from_project_id]).to eq(['is forbidden'])
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to eq(['Forked from project is forbidden'])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -245,7 +262,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be_with_reload(:namespace) { create(:group).tap { |group| group.add_owner(user) } }
|
||||
|
||||
it 'creates a fork in the group' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project.first_owner).to eq(user)
|
||||
expect(fork_of_project.namespace).to eq(namespace)
|
||||
end
|
||||
|
|
@ -255,8 +273,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
existing_project = create(:project, :repository, path: project.path, namespace: namespace)
|
||||
expect(existing_project).to be_persisted
|
||||
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors[:path]).to eq(['has already been taken'])
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to include('Path has already been taken')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -265,6 +283,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
it 'creates the project with the lower visibility level' do
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
|
@ -275,8 +294,8 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:namespace) { create(:group).tap { |group| group.add_developer(user) } }
|
||||
|
||||
it 'does not create a fork' do
|
||||
expect(fork_of_project).not_to be_persisted
|
||||
expect(fork_of_project.errors[:namespace]).to eq(['is not valid'])
|
||||
is_expected.to be_error
|
||||
expect(response.errors).to match_array(['Namespace is not valid', 'User is not allowed to import projects'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -285,8 +304,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let(:params) { super().merge(path: 'forked', name: 'My Fork', description: 'Description', visibility: 'private') }
|
||||
|
||||
it 'sets optional attributes to specified values' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.path).to eq('forked')
|
||||
expect(fork_of_project.name).to eq('My Fork')
|
||||
expect(fork_of_project.description).to eq('Description')
|
||||
|
|
@ -299,8 +319,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let(:params) { super().merge(visibility: 'unknown') }
|
||||
|
||||
it 'sets visibility level to private' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
end
|
||||
|
|
@ -311,8 +332,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let(:params) { super().merge(visibility: 'public') }
|
||||
|
||||
it 'sets visibility level to project visibility' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
end
|
||||
|
|
@ -322,8 +344,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:namespace) { create(:group, :private).tap { |group| group.add_owner(user) } }
|
||||
|
||||
it 'sets visibility level to target namespace visibility level' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
end
|
||||
|
|
@ -342,8 +365,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'copies project features visibility settings to the fork' do
|
||||
expect(fork_of_project).to be_persisted
|
||||
is_expected.to be_success
|
||||
|
||||
expect(fork_of_project).to be_persisted
|
||||
expect(fork_of_project.project_feature.slice(attrs.keys)).to eq(attrs)
|
||||
end
|
||||
end
|
||||
|
|
@ -369,7 +393,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
end
|
||||
|
||||
it 'creates a new pool repository after the project is moved to a new shard' do
|
||||
fork_before_move = subject
|
||||
fork_before_move = fork_of_project
|
||||
|
||||
storage_move = create(
|
||||
:project_repository_storage_move,
|
||||
|
|
@ -379,8 +403,10 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
)
|
||||
Projects::UpdateRepositoryStorageService.new(storage_move).execute
|
||||
|
||||
fork_after_move = described_class.new(project.reload, user, namespace: group).execute
|
||||
fork_after_move_response = described_class.new(project.reload, user, namespace: group).execute
|
||||
expect(fork_after_move_response).to be_success
|
||||
|
||||
fork_after_move = fork_after_move_response[:project]
|
||||
pool_repository_before_move = PoolRepository.joins(:shard)
|
||||
.find_by(source_project: project, shards: { name: 'default' })
|
||||
pool_repository_after_move = PoolRepository.joins(:shard)
|
||||
|
|
@ -396,8 +422,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
|
||||
context 'when no pool exists' do
|
||||
it 'creates a new object pool' do
|
||||
expect { fork_of_project }.to change { PoolRepository.count }.by(1)
|
||||
expect { response }.to change { PoolRepository.count }.by(1)
|
||||
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project.pool_repository).to eq(project.pool_repository)
|
||||
end
|
||||
|
||||
|
|
@ -405,8 +432,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let_it_be(:project) { create(:project, :private, :repository) }
|
||||
|
||||
it 'does not create an object pool' do
|
||||
expect { fork_of_project }.not_to change { PoolRepository.count }
|
||||
expect { response }.not_to change { PoolRepository.count }
|
||||
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project.pool_repository).to be_nil
|
||||
end
|
||||
end
|
||||
|
|
@ -416,8 +444,9 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
let!(:pool_repository) { create(:pool_repository, source_project: project) }
|
||||
|
||||
it 'joins the object pool' do
|
||||
expect { fork_of_project }.not_to change { PoolRepository.count }
|
||||
expect { response }.not_to change { PoolRepository.count }
|
||||
|
||||
is_expected.to be_success
|
||||
expect(fork_of_project.pool_repository).to eq(pool_repository)
|
||||
end
|
||||
end
|
||||
|
|
@ -440,7 +469,7 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
|
||||
expect(forked_from_project(unlinked_fork)).to be_nil
|
||||
|
||||
expect(service.execute(unlinked_fork)).to be_nil
|
||||
expect(service.execute(unlinked_fork)).to be_error
|
||||
|
||||
expect(forked_from_project(unlinked_fork)).to be_nil
|
||||
end
|
||||
|
|
@ -469,6 +498,21 @@ RSpec.describe Projects::ForkService, feature_category: :source_code_management
|
|||
expect(project.forks_count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when user cannot fork' do
|
||||
let(:another_user) { create(:user) }
|
||||
|
||||
it 'returns an error' do
|
||||
expect(unlinked_fork.forked?).to be_falsey
|
||||
expect(forked_from_project(unlinked_fork)).to be_nil
|
||||
|
||||
response = described_class.new(project, another_user, params).execute(unlinked_fork)
|
||||
expect(response).to be_error
|
||||
expect(response.errors).to eq ['Forked from project is forbidden']
|
||||
|
||||
expect(forked_from_project(unlinked_fork)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'if the fork is not allowed' do
|
||||
let_it_be(:project) { create(:project, :private) }
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,13 @@ module ProjectForksHelper
|
|||
allow(service).to receive(:gitlab_shell).and_return(shell)
|
||||
end
|
||||
|
||||
forked_project = service.execute(params[:target_project])
|
||||
response = service.execute(params[:target_project])
|
||||
|
||||
# This helper is expected to return a valid result.
|
||||
# This exception will be raised if someone tries to test failed states using fork_project method (not recommended).
|
||||
raise ArgumentError, response.message if response.error?
|
||||
|
||||
forked_project = response[:project]
|
||||
|
||||
# Reload the both projects so they know about their newly created fork_network
|
||||
if forked_project.persisted?
|
||||
|
|
|
|||
Loading…
Reference in New Issue