Commit e9c2bf26 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 946771d0
......@@ -26,6 +26,10 @@ export default (resolvers = {}, config = {}) => {
headers: {
[csrf.headerKey]: csrf.token,
},
// fetch won’t send cookies in older browsers, unless you set the credentials init option.
// We set to `same-origin` which is default value in modern browsers.
// See https://github.com/whatwg/fetch/pull/585 for more information.
credentials: 'same-origin',
};
return new ApolloClient({
......
......@@ -39,7 +39,7 @@ class Projects::ForksController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def new
@namespaces = fork_service.valid_fork_targets
@namespaces = fork_service.valid_fork_targets - [project.namespace]
end
# rubocop: disable CodeReuse/ActiveRecord
......
......@@ -8,7 +8,7 @@ class ForkTargetsFinder
# rubocop: disable CodeReuse/ActiveRecord
def execute
::Namespace.where(id: user.manageable_namespaces).where.not(id: project.namespace).sort_by_type
::Namespace.where(id: user.manageable_namespaces).sort_by_type
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -62,6 +62,10 @@ module Ci
end
end
def has_downstream_pipeline?
sourced_pipelines.exists?
end
def downstream_pipeline_params
return child_params if triggers_child_pipeline?
return cross_project_params if downstream_project.present?
......
......@@ -227,6 +227,7 @@ module Ci
end
after_transition created: :pending do |pipeline|
next if Feature.enabled?(:ci_drop_bridge_on_downstream_errors, pipeline.project, default_enabled: true)
next unless pipeline.bridge_triggered?
next if pipeline.bridge_waiting?
......@@ -756,6 +757,8 @@ module Ci
raise BridgeStatusError unless source_bridge.active?
source_bridge.success!
rescue => e
Gitlab::ErrorTracking.track_exception(e, pipeline_id: id)
end
def bridge_triggered?
......@@ -774,6 +777,10 @@ module Ci
child_pipelines.exists?
end
def created_successfully?
persisted? && failure_reason.blank?
end
def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user)
......
......@@ -188,14 +188,6 @@ class Snippet < ApplicationRecord
end
end
def self.content_types
[
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb",
".js", ".sh", ".coffee", ".yml", ".md"
]
end
def blob
@blob ||= Blob.decorate(SnippetBlob.new(self), self)
end
......
......@@ -5,9 +5,19 @@ module Ci
class CreateCrossProjectPipelineService < ::BaseService
include Gitlab::Utils::StrongMemoize
DuplicateDownstreamPipelineError = Class.new(StandardError)
def execute(bridge)
@bridge = bridge
if bridge.has_downstream_pipeline?
Gitlab::ErrorTracking.track_exception(
DuplicateDownstreamPipelineError.new,
bridge_id: @bridge.id, project_id: @bridge.project_id
)
return
end
pipeline_params = @bridge.downstream_pipeline_params
target_ref = pipeline_params.dig(:target_revision, :ref)
......@@ -18,14 +28,32 @@ module Ci
current_user,
pipeline_params.fetch(:target_revision))
service.execute(
downstream_pipeline = service.execute(
pipeline_params.fetch(:source), pipeline_params[:execute_params]) do |pipeline|
pipeline.variables.build(@bridge.downstream_variables)
end
downstream_pipeline.tap do |pipeline|
next if Feature.disabled?(:ci_drop_bridge_on_downstream_errors, project, default_enabled: true)
update_bridge_status!(@bridge, pipeline)
end
end
private
def update_bridge_status!(bridge, pipeline)
Gitlab::OptimisticLocking.retry_lock(bridge) do |subject|
if pipeline.created_successfully?
# If bridge uses `strategy:depend` we leave it running
# and update the status when the downstream pipeline completes.
subject.success! unless subject.dependent?
else
subject.drop!(:downstream_pipeline_creation_failed)
end
end
end
def ensure_preconditions!(target_ref)
unless downstream_project_accessible?
@bridge.drop!(:downstream_bridge_project_not_found)
......
---
title: Allow to fork to the same namespace and different path via API call
merge_request: 26062
author:
type: fixed
---
title: Project Snippets API endpoints check feature status
merge_request: 26064
author:
type: performance
---
title: Add support for configuring remote mirrors via API
merge_request: 25825
author: Rajendra Kadam
type: added
---
title: Drop bridge if downstream pipeline has errors
merge_request: 25706
author:
type: fixed
---
title: Add avatar upload support for create and update group APIs
merge_request: 25751
author: Rajendra Kadam
type: added
---
title: Add web_url attribute to API response for Commits
merge_request: 26173
author:
type: added
---
title: Remove unused Snippets#content_types method
merge_request: 26306
author:
type: other
---
title: Send credentials with GraphQL fetch requests
merge_request: 26386
author:
type: fixed
......@@ -79,6 +79,8 @@ From there, you can see the following actions:
- Release was added to a project
- Release was updated
- Release milestone associations changed
- Permission to approve merge requests by committers was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/issues/7531) in GitLab 12.9)
- Permission to approve merge requests by authors was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/issues/7531) in GitLab 12.9)
### Instance events **(PREMIUM ONLY)**
......
This diff is collapsed.
......@@ -42,7 +42,8 @@ Example response:
"message": "Replace sanitize with escape once",
"parent_ids": [
"6104942438c14ec7bd21c6cd5bd995272b3faff6"
]
],
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746"
},
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
......@@ -56,7 +57,8 @@ Example response:
"message": "Sanitize for network graph",
"parent_ids": [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
]
],
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746"
}
]
```
......@@ -156,7 +158,8 @@ Example response:
"deletions": 2,
"total": 4
},
"status": null
"status": null,
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746"
}
```
......@@ -235,7 +238,8 @@ Example response:
"deletions": 10,
"total": 25
},
"status": "running"
"status": "running",
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/6104942438c14ec7bd21c6cd5bd995272b3faff6"
}
```
......@@ -314,7 +318,8 @@ Example response:
"message": "Feature added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n",
"parent_ids": [
"a738f717824ff53aebad8b090c1b79a14f2bd9e8"
]
],
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/8b090c1b79a14f2bd9e8a738f717824ff53aebad"
}
```
......@@ -370,7 +375,8 @@ Example response:
"authored_date":"2018-11-08T15:55:26.000Z",
"committer_name":"Administrator",
"committer_email":"admin@example.com",
"committed_date":"2018-11-08T15:55:26.000Z"
"committed_date":"2018-11-08T15:55:26.000Z",
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/8b090c1b79a14f2bd9e8a738f717824ff53aebad"
}
```
......
......@@ -492,6 +492,7 @@ Parameters:
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
| `subgroup_creation_level` | string | no | Allowed to create subgroups. Can be `owner` (Owners), or `maintainer` (Maintainers). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
......@@ -553,6 +554,7 @@ PUT /groups/:id
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
| `subgroup_creation_level` | string | no | Allowed to create subgroups. Can be `owner` (Owners), or `maintainer` (Maintainers). |
| `emails_disabled` | boolean | no | Disable email notifications |
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/issues/36681) |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
......
......@@ -9,6 +9,10 @@ module API
expose :safe_message, as: :message
expose :author_name, :author_email, :authored_date
expose :committer_name, :committer_email, :committed_date
expose :web_url do |commit, _options|
Gitlab::UrlBuilder.build(commit)
end
end
end
end
......@@ -11,6 +11,8 @@ module API
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the group'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for the group' # rubocop:disable Scalability/FileUploads
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users in this group to setup Two-factor authentication'
optional :two_factor_grace_period, type: Integer, desc: 'Time before Two-factor authentication is enforced'
......
......@@ -5,12 +5,17 @@ module API
include PaginationParams
before { authenticate! }
before { check_snippets_enabled }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
helpers do
def check_snippets_enabled
forbidden! unless user_project.feature_available?(:snippets, current_user)
end
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
......
......@@ -26,6 +26,26 @@ module API
with: Entities::RemoteMirror
end
desc 'Create remote mirror for a project' do
success Entities::RemoteMirror
end
params do
requires :url, type: String, desc: 'The URL for a remote mirror'
optional :enabled, type: Boolean, desc: 'Determines if the mirror is enabled'
optional :only_protected_branches, type: Boolean, desc: 'Determines if only protected branches are mirrored'
end
post ':id/remote_mirrors' do
create_params = declared_params(include_missing: false)
new_mirror = user_project.remote_mirrors.create(create_params)
if new_mirror.persisted?
present new_mirror, with: Entities::RemoteMirror
else
render_validation_error!(new_mirror)
end
end
desc 'Update the attributes of a single remote mirror' do
success Entities::RemoteMirror
end
......
......@@ -13,7 +13,8 @@ module Gitlab
end
def url
case object
# Objects are sometimes wrapped in a BatchLoader instance
case object.itself
when Commit
commit_url
when Issue
......@@ -33,7 +34,7 @@ module Gitlab
when User
user_url(object)
else
raise NotImplementedError.new("No URL builder defined for #{object.class}")
raise NotImplementedError.new("No URL builder defined for #{object.inspect}")
end
end
......
......@@ -28,8 +28,8 @@ describe ForkTargetsFinder do
end
describe '#execute' do
it 'returns all user manageable namespaces except project namespace' do
expect(finder.execute).to match_array([user.namespace, maintained_group, owned_group])
it 'returns all user manageable namespaces' do
expect(finder.execute).to match_array([user.namespace, maintained_group, owned_group, project.namespace])
end
end
end
......@@ -12,7 +12,8 @@
"authored_date",
"committer_name",
"committer_email",
"committed_date"
"committed_date",
"web_url"
],
"properties" : {
"id": { "type": ["string", "null"] },
......@@ -32,6 +33,7 @@
"authored_date": { "type": "date" },
"committer_name": { "type": "string" },
"committer_email": { "type": "string" },
"committed_date": { "type": "date" }
"committed_date": { "type": "date" },
"web_url": { "type": "string" }
}
}
......@@ -14,6 +14,18 @@ describe Gitlab::UrlBuilder do
end
end
context 'when passing a batch loaded Commit' do
it 'returns a proper URL' do
commit = BatchLoader.for(:commit).batch do |batch, loader|
batch.each { |commit| loader.call(:commit, build_stubbed(:commit)) }
end
url = described_class.build(commit)
expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.full_path}/-/commit/#{commit.id}"
end
end
context 'when passing an Issue' do
it 'returns a proper URL' do
issue = build_stubbed(:issue, iid: 42)
......@@ -160,7 +172,7 @@ describe Gitlab::UrlBuilder do
project = build_stubbed(:project)
expect { described_class.build(project) }
.to raise_error(NotImplementedError, 'No URL builder defined for Project')
.to raise_error(NotImplementedError, "No URL builder defined for #{project.inspect}")
end
end
end
......
......@@ -2813,6 +2813,30 @@ describe Ci::Pipeline, :mailer do
end
end
describe '#created_successfully?' do
subject { pipeline.created_successfully? }
context 'when pipeline is not persisted' do
let(:pipeline) { build(:ci_pipeline) }
it { is_expected.to be_falsey }
end
context 'when pipeline is persisted' do
context 'when pipeline has failure reasons' do
let(:pipeline) { create(:ci_pipeline, failure_reason: :config_error) }
it { is_expected.to be_falsey }
end
context 'when pipeline has no failure reasons' do
let(:pipeline) { create(:ci_pipeline, failure_reason: nil) }
it { is_expected.to be_truthy }
end
end
end
describe '#parent_pipeline' do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
......@@ -2960,8 +2984,7 @@ describe Ci::Pipeline, :mailer do
it 'can not update bridge status if is not active' do
bridge.success!
expect { pipeline.update_bridge_status! }
.to raise_error Ci::Pipeline::BridgeStatusError
expect { pipeline.update_bridge_status! }.not_to change { bridge.status }
end
end
end
......@@ -2992,9 +3015,12 @@ describe Ci::Pipeline, :mailer do
end
describe '#update_bridge_status!' do
it 'can not update upstream job status' do
expect { pipeline.update_bridge_status! }
.to raise_error ArgumentError
it 'tracks an ArgumentError and does not update upstream job status' do
expect(Gitlab::ErrorTracking)
.to receive(:track_exception)
.with(instance_of(ArgumentError), pipeline_id: pipeline.id)
pipeline.update_bridge_status!
end
end
end
......
......@@ -21,6 +21,47 @@ describe API::Groups do
group2.add_owner(user2)
end
shared_examples 'group avatar upload' do
context 'when valid' do
let(:file_path) { 'spec/fixtures/banana_sample.gif' }
it 'returns avatar url in response' do
make_upload_request
group_id = json_response['id']
expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
'-/system/group/avatar/'\
"#{group_id}/banana_sample.gif")
end
end
context 'when invalid' do
shared_examples 'invalid file upload request' do
it 'returns 400' do
make_upload_request
expect(response).to have_gitlab_http_status(:bad_request)
expect(response.message).to eq('Bad Request')
expect(json_response['message'].to_s).to match(/#{message}/)
end
end
context 'when file format is not supported' do
let(:file_path) { 'spec/fixtures/doc_sample.txt' }
let(:message) { 'file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico' }
it_behaves_like 'invalid file upload request'
end
context 'when file format is not supported' do
let(:file_path) { 'spec/fixtures/big-image.png' }
let(:message) { 'is too big' }
it_behaves_like 'invalid file upload request'
end
end
end
describe "GET /groups" do
context "when unauthenticated" do
it "returns public groups" do
......@@ -539,6 +580,15 @@ describe API::Groups do
describe 'PUT /groups/:id' do
let(:new_group_name) { 'New Group'}
it_behaves_like 'group avatar upload' do
def make_upload_request
group_param = {
avatar: fixture_file_upload(file_path)
}
put api("/groups/#{group1.id}", user1), params: group_param
end
end
context 'when authenticated as the group owner' do
it 'updates the group' do
put api("/groups/#{group1.id}", user1), params: {
......@@ -940,6 +990,16 @@ describe API::Groups do
end
describe "POST /groups" do
it_behaves_like 'group avatar upload' do
def make_upload_request
params = attributes_for_group_api(request_access_enabled: false).tap do |attrs|
attrs[:avatar] = fixture_file_upload(file_path)
end
post api("/groups", user3), params: params
end
end
context "when authenticated as user without group permissions" do
it "does not create group" do
group = attributes_for_group_api
......
......@@ -1150,12 +1150,16 @@ describe API::MergeRequests do
describe 'POST /projects/:id/merge_requests/:merge_request_iid/pipelines' do
before do
stub_ci_pipeline_yaml_file(YAML.dump({
stub_ci_pipeline_yaml_file(ci_yaml)
end
let(:ci_yaml) do
YAML.dump({
rspec: {
script: 'ls',
only: ['merge_requests']
}
}))
})
end
let(:project) do
......@@ -1208,6 +1212,18 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when the .gitlab-ci.yml file is invalid' do
let(:ci_yaml) { 'invalid yaml file' }
it 'creates a failed pipeline' do
expect { request }.to change(Ci::Pipeline, :count).by(1)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_a Hash
expect(merge_request.pipelines_for_merge_request.last).to be_failed
expect(merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
end
describe 'POST /projects/:id/merge_requests' do
......
......@@ -6,6 +6,12 @@ describe API::ProjectSnippets do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:project_no_snippets) { create(:project, :snippets_disabled) }
before do
project_no_snippets.add_developer(admin)
project_no_snippets.add_developer(user)
end
describe "GET /projects/:project_id/snippets/:id/user_agent_detail" do
let(:snippet) { create(:project_snippet, :public, project: project) }
......@@ -32,6 +38,12 @@ describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/123/user_agent_detail", admin) }
end
end
end
describe 'GET /projects/:project_id/snippets/' do
......@@ -63,6 +75,12 @@ describe API::ProjectSnippets do
expect(json_response).to be_an Array
expect(json_response.size).to eq(0)
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets", user) }
end
end
end
describe 'GET /projects/:project_id/snippets/:id' do
......@@ -85,6 +103,12 @@ describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Not found')
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/123", user) }
end
end
end
describe 'POST /projects/:project_id/snippets/' do
......@@ -244,11 +268,17 @@ describe API::ProjectSnippets do
end
end
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { post api("/projects/#{project_no_snippets.id}/snippets", user), params: params }
end
end
end
describe 'PUT /projects/:project_id/snippets/:id/' do
let(:visibility_level) { Snippet::PUBLIC }
let(:snippet) { create(:project_snippet, author: admin, visibility_level: visibility_level) }
let(:snippet) { create(:project_snippet, author: admin, visibility_level: visibility_level, project: project) }
it 'updates snippet' do
new_content = 'New content'
......@@ -354,10 +384,16 @@ describe API::ProjectSnippets do
end
end
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { put api("/projects/#{project_no_snippets.id}/snippets/123", admin), params: { description: 'foo' } }
end
end
end
describe 'DELETE /projects/:project_id/snippets/:id/' do
let(:snippet) { create(:project_snippet, author: admin) }
let(:snippet) { create(:project_snippet, author: admin, project: project) }
it 'deletes snippet' do
delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin)
......@@ -375,10 +411,16 @@ describe API::ProjectSnippets do
it_behaves_like '412 response' do
let(:request) { api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) }
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { delete api("/projects/#{project_no_snippets.id}/snippets/123", admin) }
end
end
end
describe 'GET /projects/:project_id/snippets/:id/raw' do
let(:snippet) { create(:project_snippet, author: admin) }
let(:snippet) { create(:project_snippet, author: admin, project: project) }
it 'returns raw text' do
get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin)
......@@ -394,5 +436,11 @@ describe API::ProjectSnippets do
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/123/raw", admin) }
end
end
end
end
......@@ -2935,6 +2935,26 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(:conflict)
expect(json_response['message']['name']).to eq(['has already been taken'])
end
it 'forks to the same namespace with alternative path and name' do
post api("/projects/#{project.id}/fork", user), params: { path: 'path_2', name: 'name_2' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq('name_2')
expect(json_response['path']).to eq('path_2')
expect(json_response['owner']['id']).to eq(user.id)
expect(json_response['namespace']['id']).to eq(user.namespace.id)
expect(json_response['forked_from_project']['id']).to eq(project.id)
expect(json_response['import_status']).to eq('scheduled')
end
it 'fails to fork to the same namespace without alternative path and name' do
post api("/projects/#{project.id}/fork", 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'])
end
end
context 'when unauthenticated' do
......
......@@ -39,6 +39,54 @@ describe API::RemoteMirrors do
end
end
describe 'POST /projects/:id/remote_mirrors' do
let(:route) { "/projects/#{project.id}/remote_mirrors" }
shared_examples 'creates a remote mirror' do
it 'creates a remote mirror and returns reponse' do
project.add_maintainer(user)
post api(route, user), params: params
enabled = params.fetch(:enabled, false)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('remote_mirror')
expect(json_response['enabled']).to eq(enabled)
end
end
it 'requires `admin_remote_mirror` permission' do
post api(route, developer)
expect(response).to have_gitlab_http_status(:unauthorized)
end
context 'creates a remote mirror' do
context 'disabled by default' do
let(:params) { { url: 'https://foo:bar@test.com' } }
it_behaves_like 'creates a remote mirror'
end
context 'enabled' do
let(:params) { { url: 'https://foo:bar@test.com', enabled: true } }
it_behaves_like 'creates a remote mirror'
end
end
it 'returns error if url is invalid' do
project.add_maintainer(user)
post api(route, user), params: {
url: 'ftp://foo:bar@test.com'
}
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['url']).to eq(["is blocked: Only allowed schemes are ssh, git, http, https"])
end
end
describe 'PUT /projects/:id/remote_mirrors/:mirror_id' do
let(:route) { ->(id) { "/projects/#{project.id}/remote_mirrors/#{id}" } }
let(:mirror) { project.remote_mirrors.first }
......
......@@ -116,6 +116,28 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
expect(bridge.reload).to be_success
end
context 'when bridge job has already any downstream pipelines' do
before do
bridge.sourced_pipelines.create!(
source_pipeline: bridge.pipeline,
source_project: bridge.project,
project: bridge.project,
pipeline: create(:ci_pipeline, project: bridge.project)
)
end
it 'logs an error and exits' do
expect(Gitlab::ErrorTracking)
.to receive(:track_exception)
.with(
instance_of(Ci::CreateCrossProjectPipelineService::DuplicateDownstreamPipelineError),
bridge_id: bridge.id, project_id: bridge.project.id)
.and_call_original
expect(Ci::CreatePipelineService).not_to receive(:new)
expect(service.execute(bridge)).to be_nil
end
end
context 'when target ref is not specified' do
let(:trigger) do
{ trigger: { project: downstream_project.full_path } }
......@@ -149,13 +171,11 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
expect(pipeline.source_bridge).to be_a ::Ci::Bridge
end
it 'does not update bridge status when downstream pipeline gets processed' do
it 'updates the bridge status when downstream pipeline gets processed' do
pipeline = service.execute(bridge)
expect(pipeline.reload).to be_failed
# TODO: This should change to failed once #198354 gets fixed.
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25706
expect(bridge.reload).to be_pending
expect(bridge.reload).to be_failed
end
end
......@@ -242,6 +262,22 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline'
it 'updates the bridge job to success' do
expect { service.execute(bridge) }.to change { bridge.status }.to 'success'
end
context 'when bridge uses "depend" strategy' do
let(:trigger) do
{
trigger: { include: 'child-pipeline.yml', strategy: 'depend' }
}
end
it 'does not update the bridge job status' do
expect { service.execute(bridge) }.not_to change { bridge.status }
end
end
context 'when latest sha for the ref changed in the meantime' do
before do
upstream_project.repository.create_file(
......@@ -298,6 +334,34 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end
end
context 'when downstream pipeline creation errors out' do
let(:stub_config) { false }
before do
stub_ci_pipeline_yaml_file(YAML.dump(invalid: { yaml: 'error' }))
end
it 'creates only one new pipeline' do
expect { service.execute(bridge) }
.to change { Ci::Pipeline.count }.by(1)
end
it 'creates a new pipeline in the downstream project' do
pipeline = service.execute(bridge)
expect(pipeline.user).to eq bridge.user
expect(pipeline.project).to eq downstream_project
end
it 'drops the bridge' do
pipeline = service.execute(bridge)
expect(pipeline.reload).to be_failed
expect(bridge.reload).to be_failed
expect(bridge.failure_reason).to eq('downstream_pipeline_creation_failed')
end
end
context 'when bridge job has YAML variables defined' do
before do
bridge.yaml_variables = [{ key: 'BRIDGE', value: 'var', public: true }]
......
......@@ -129,6 +129,21 @@ describe Git::BranchPushService, services: true do
end
end
end
context 'when .gitlab-ci.yml file is invalid' do
before do
stub_ci_pipeline_yaml_file('invalid yaml file')
end
it 'persists an error pipeline' do
expect { subject }.to change { Ci::Pipeline.count }
pipeline = Ci::Pipeline.last
expect(pipeline).to be_push
expect(pipeline).to be_failed
expect(pipeline).to be_config_error
end
end
end
describe "Updates merge requests" do
......
......@@ -177,18 +177,18 @@ describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
describe 'Pipelines for merge requests' do
before do
stub_ci_pipeline_yaml_file(YAML.dump(config))
stub_ci_pipeline_yaml_file(config)
end
context "when .gitlab-ci.yml has merge_requests keywords" do
let(:config) do
{
YAML.dump({
test: {
stage: 'test',
script: 'echo',
only: ['merge_requests']
}
}
})
end
it 'creates a detached merge request pipeline and sets it as a head pipeline' do
......@@ -269,12 +269,12 @@ describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
context "when .gitlab-ci.yml does not have merge_requests keywords" do
let(:config) do
{
YAML.dump({
test: {
stage: 'test',
script: 'echo'
}
}
})
end
it 'does not create a detached merge request pipeline' do
......@@ -284,6 +284,19 @@ describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
expect(merge_request.pipelines_for_merge_request.count).to eq(0)
end
end
context 'when .gitlab-ci.yml is invalid' do
let(:config) { 'invalid yaml file' }
it 'persists a pipeline with config error' do
expect(merge_request).to be_persisted
merge_request.reload
expect(merge_request.pipelines_for_merge_request.count).to eq(1)
expect(merge_request.pipelines_for_merge_request.last).to be_failed
expect(merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
end
it 'increments the usage data counter of create event' do
......
......@@ -148,7 +148,7 @@ describe MergeRequests::RefreshService do
describe 'Pipelines for merge requests' do
before do
stub_ci_pipeline_yaml_file(YAML.dump(config))
stub_ci_pipeline_yaml_file(config)
end
subject { service.new(project, @user).execute(@oldrev, @newrev, ref) }
......@@ -158,13 +158,13 @@ describe MergeRequests::RefreshService do
context "when .gitlab-ci.yml has merge_requests keywords" do
let(:config) do
{
YAML.dump({
test: {
stage: 'test',
script: 'echo',
only: ['merge_requests']
}
}
})
end
it 'create detached merge request pipeline with commits' do
......@@ -255,16 +255,28 @@ describe MergeRequests::RefreshService do
end.not_to change { @merge_request.pipelines_for_merge_request.count }
end
end
context 'when the pipeline should be skipped' do
it 'saves a skipped detached merge request pipeline' do
project.repository.create_file(@user, 'new-file.txt', 'A new file',
message: '[skip ci] This is a test',
branch_name: 'master')
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_skipped
end
end
end
context "when .gitlab-ci.yml does not have merge_requests keywords" do
let(:config) do
{
YAML.dump({
test: {
stage: 'test',
script: 'echo'
}
}
})
end
it 'does not create a detached merge request pipeline' do
......@@ -272,6 +284,40 @@ describe MergeRequests::RefreshService do
.not_to change { @merge_request.pipelines_for_merge_request.count }
end
end
context 'when .gitlab-ci.yml is invalid' do
let(:config) { 'invalid yaml file' }
it 'persists a pipeline with config error' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_failed
expect(@merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
context 'when .gitlab-ci.yml file is valid but has a logical error' do
let(:config) do
YAML.dump({
build: {
script: 'echo "Valid yaml syntax, but..."',
only: ['master']
},
test: {
script: 'echo "... I depend on build, which does not run."',
only: ['merge_request'],
needs: ['build']
}
})
end
it 'persists a pipeline with config error' do
expect { subject }
.to change { @merge_request.pipelines_for_merge_request.count }.by(1)
expect(@merge_request.pipelines_for_merge_request.last).to be_failed
expect(@merge_request.pipelines_for_merge_request.last).to be_config_error
end
end
end
context 'push to origin repo source branch' do
......
......@@ -796,10 +796,10 @@
dependencies:
vue-eslint-parser "^7.0.0"
"@gitlab/svgs@^1.104.0":
version "1.104.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.104.0.tgz#ebbf99788d74b7224f116f1c0040fa0c90034d99"
integrity sha512-lWg/EzxFdbx4YIdDWB2p5ag6Cna78AYGET8nXQYXYwd21/U3wKXKL7vsGR4kOxe1goA9ZAYG9eY+MK7cf+X2cA==
"@gitlab/svgs@^1.105.0":
version "1.105.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.105.0.tgz#9686f8696594a5f22de11af2b81fdcceb715f4f2"
integrity sha512-2wzZXe2b7DnGyL7FTbPq0dSpk+gjkq4SBTNtMrqdwX2qaM+XJB50XaMm17kdY5V1bBkMgbc7JJ2vgbLxhS/CkQ==
"@gitlab/ui@^9.20.0":
version "9.20.0"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment