Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
2eb74fb7
Commit
2eb74fb7
authored
Mar 29, 2019
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab-ce master
parents
38921267
0d26c483
Changes
28
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
403 additions
and
107 deletions
+403
-107
app/models/ci/build.rb
app/models/ci/build.rb
+4
-2
app/models/ci/pipeline.rb
app/models/ci/pipeline.rb
+8
-0
app/models/merge_request.rb
app/models/merge_request.rb
+4
-0
app/presenters/ci/build_runner_presenter.rb
app/presenters/ci/build_runner_presenter.rb
+8
-1
app/services/merge_requests/base_service.rb
app/services/merge_requests/base_service.rb
+19
-7
app/services/merge_requests/create_service.rb
app/services/merge_requests/create_service.rb
+1
-1
app/services/merge_requests/refresh_service.rb
app/services/merge_requests/refresh_service.rb
+1
-1
changelogs/unreleased/persist-fulll-ref-path-for-mr-pipelines.yml
...gs/unreleased/persist-fulll-ref-path-for-mr-pipelines.yml
+5
-0
lib/gitlab/ci/pipeline/chain/command.rb
lib/gitlab/ci/pipeline/chain/command.rb
+7
-0
lib/gitlab/ci/pipeline/chain/validate/abilities.rb
lib/gitlab/ci/pipeline/chain/validate/abilities.rb
+2
-0
lib/gitlab/ci/pipeline/chain/validate/repository.rb
lib/gitlab/ci/pipeline/chain/validate/repository.rb
+1
-1
spec/factories/merge_requests.rb
spec/factories/merge_requests.rb
+14
-3
spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+18
-0
spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+46
-1
spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
.../lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
+19
-0
spec/migrations/migrate_cluster_configure_worker_sidekiq_queue_spec.rb
...ns/migrate_cluster_configure_worker_sidekiq_queue_spec.rb
+10
-16
spec/migrations/migrate_create_trace_artifact_sidekiq_queue_spec.rb
...tions/migrate_create_trace_artifact_sidekiq_queue_spec.rb
+9
-15
spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb
...tions/migrate_object_storage_upload_sidekiq_queue_spec.rb
+3
-9
spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb
spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb
+7
-13
spec/migrations/migrate_storage_migrator_sidekiq_queue_spec.rb
...migrations/migrate_storage_migrator_sidekiq_queue_spec.rb
+3
-9
spec/migrations/migrate_update_head_pipeline_for_merge_request_sidekiq_queue_spec.rb
...ate_head_pipeline_for_merge_request_sidekiq_queue_spec.rb
+8
-14
spec/models/ci/build_spec.rb
spec/models/ci/build_spec.rb
+20
-0
spec/models/ci/pipeline_spec.rb
spec/models/ci/pipeline_spec.rb
+36
-0
spec/models/merge_request_spec.rb
spec/models/merge_request_spec.rb
+28
-0
spec/presenters/ci/build_runner_presenter_spec.rb
spec/presenters/ci/build_runner_presenter_spec.rb
+38
-0
spec/services/merge_requests/create_service_spec.rb
spec/services/merge_requests/create_service_spec.rb
+41
-7
spec/services/merge_requests/refresh_service_spec.rb
spec/services/merge_requests/refresh_service_spec.rb
+34
-7
spec/support/helpers/stub_worker.rb
spec/support/helpers/stub_worker.rb
+9
-0
No files found.
app/models/ci/build.rb
View file @
2eb74fb7
...
...
@@ -26,7 +26,8 @@ module Ci
belongs_to
:erased_by
,
class_name:
'User'
RUNNER_FEATURES
=
{
upload_multiple_artifacts:
->
(
build
)
{
build
.
publishes_artifacts_reports?
}
upload_multiple_artifacts:
->
(
build
)
{
build
.
publishes_artifacts_reports?
},
refspecs:
->
(
build
)
{
build
.
merge_request_ref?
}
}.
freeze
has_one
:deployment
,
as: :deployable
,
class_name:
'Deployment'
...
...
@@ -47,7 +48,8 @@ module Ci
delegate
:terminal_specification
,
to: :runner_session
,
allow_nil:
true
delegate
:gitlab_deploy_token
,
to: :project
delegate
:trigger_short_token
,
to: :trigger_request
,
allow_nil:
true
delegate
:merge_request_event?
,
to: :pipeline
delegate
:merge_request_event?
,
:merge_request_ref?
,
:legacy_detached_merge_request_pipeline?
,
to: :pipeline
##
# Since Gitlab 11.5, deployments records started being created right after
...
...
app/models/ci/pipeline.rb
View file @
2eb74fb7
...
...
@@ -738,6 +738,10 @@ module Ci
triggered_by_merge_request?
&&
target_sha
.
nil?
end
def
legacy_detached_merge_request_pipeline?
detached_merge_request_pipeline?
&&
!
merge_request_ref?
end
def
merge_request_pipeline?
triggered_by_merge_request?
&&
target_sha
.
present?
end
...
...
@@ -746,6 +750,10 @@ module Ci
triggered_by_merge_request?
&&
target_sha
==
merge_request
.
target_branch_sha
end
def
merge_request_ref?
MergeRequest
.
merge_request_ref?
(
ref
)
end
def
matches_sha_or_source_sha?
(
sha
)
self
.
sha
==
sha
||
self
.
source_sha
==
sha
end
...
...
app/models/merge_request.rb
View file @
2eb74fb7
...
...
@@ -1131,6 +1131,10 @@ class MergeRequest < ApplicationRecord
"refs/
#{
Repository
::
REF_MERGE_REQUEST
}
/
#{
iid
}
/merge"
end
def
self
.
merge_request_ref?
(
ref
)
ref
.
start_with?
(
"refs/
#{
Repository
::
REF_MERGE_REQUEST
}
/"
)
end
def
in_locked_state
begin
lock_mr
...
...
app/presenters/ci/build_runner_presenter.rb
View file @
2eb74fb7
...
...
@@ -4,6 +4,7 @@ module Ci
class
BuildRunnerPresenter
<
SimpleDelegator
include
Gitlab
::
Utils
::
StrongMemoize
DEFAULT_GIT_DEPTH_MERGE_REQUEST
=
10
RUNNER_REMOTE_TAG_PREFIX
=
'refs/tags/'
.
freeze
RUNNER_REMOTE_BRANCH_PREFIX
=
'refs/remotes/origin/'
.
freeze
...
...
@@ -27,6 +28,7 @@ module Ci
def
git_depth
strong_memoize
(
:git_depth
)
do
git_depth
=
variables
&
.
find
{
|
variable
|
variable
[
:key
]
==
'GIT_DEPTH'
}
&
.
dig
(
:value
)
git_depth
||=
DEFAULT_GIT_DEPTH_MERGE_REQUEST
if
merge_request_ref?
git_depth
.
to_i
end
end
...
...
@@ -35,8 +37,9 @@ module Ci
specs
=
[]
if
git_depth
>
0
specs
<<
refspec_for_branch
(
ref
)
if
branch?
||
merge_request_event
?
specs
<<
refspec_for_branch
(
ref
)
if
branch?
||
legacy_detached_merge_request_pipeline
?
specs
<<
refspec_for_tag
(
ref
)
if
tag?
specs
<<
refspec_for_merge_request_ref
if
merge_request_ref?
else
specs
<<
refspec_for_branch
specs
<<
refspec_for_tag
...
...
@@ -83,5 +86,9 @@ module Ci
def
refspec_for_tag
(
ref
=
'*'
)
"+
#{
Gitlab
::
Git
::
TAG_REF_PREFIX
}#{
ref
}
:
#{
RUNNER_REMOTE_TAG_PREFIX
}#{
ref
}
"
end
def
refspec_for_merge_request_ref
"+
#{
ref
}
:
#{
ref
}
"
end
end
end
app/services/merge_requests/base_service.rb
View file @
2eb74fb7
...
...
@@ -54,7 +54,7 @@ module MergeRequests
merge_request
,
merge_request
.
project
,
current_user
,
merge_request
.
assignee
)
end
def
create_
merge_request_pipeline
(
merge_request
,
user
)
def
create_
pipeline_for
(
merge_request
,
user
)
return
unless
Feature
.
enabled?
(
:ci_merge_request_pipeline
,
merge_request
.
source_project
,
default_enabled:
true
)
...
...
@@ -65,12 +65,24 @@ module MergeRequests
return
if
merge_request
.
merge_request_pipeline_exists?
return
if
merge_request
.
has_no_commits?
Ci
::
CreatePipelineService
.
new
(
merge_request
.
source_project
,
user
,
ref:
merge_request
.
source_branch
)
.
execute
(
:merge_request_event
,
ignore_skip_ci:
true
,
save_on_errors:
false
,
merge_request:
merge_request
)
create_detached_merge_request_pipeline
(
merge_request
,
user
)
end
def
create_detached_merge_request_pipeline
(
merge_request
,
user
)
if
can_use_merge_request_ref?
(
merge_request
)
Ci
::
CreatePipelineService
.
new
(
merge_request
.
source_project
,
user
,
ref:
merge_request
.
ref_path
)
.
execute
(
:merge_request_event
,
merge_request:
merge_request
)
else
Ci
::
CreatePipelineService
.
new
(
merge_request
.
source_project
,
user
,
ref:
merge_request
.
source_branch
)
.
execute
(
:merge_request_event
,
merge_request:
merge_request
)
end
end
def
can_use_merge_request_ref?
(
merge_request
)
Feature
.
enabled?
(
:ci_use_merge_request_ref
,
project
,
default_enabled:
true
)
&&
!
merge_request
.
for_fork?
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
...
...
app/services/merge_requests/create_service.rb
View file @
2eb74fb7
...
...
@@ -25,7 +25,7 @@ module MergeRequests
def
after_create
(
issuable
)
todo_service
.
new_merge_request
(
issuable
,
current_user
)
issuable
.
cache_merge_request_closes_issues!
(
current_user
)
create_
merge_request_pipeline
(
issuable
,
current_user
)
create_
pipeline_for
(
issuable
,
current_user
)
issuable
.
update_head_pipeline
super
...
...
app/services/merge_requests/refresh_service.rb
View file @
2eb74fb7
...
...
@@ -107,7 +107,7 @@ module MergeRequests
end
merge_request
.
mark_as_unchecked
create_
merge_request_pipeline
(
merge_request
,
current_user
)
create_
pipeline_for
(
merge_request
,
current_user
)
UpdateHeadPipelineForMergeRequestWorker
.
perform_async
(
merge_request
.
id
)
end
...
...
changelogs/unreleased/persist-fulll-ref-path-for-mr-pipelines.yml
0 → 100644
View file @
2eb74fb7
---
title
:
Create MR pipelines with `refs/merge-requests/:iid/head`
merge_request
:
25504
author
:
type
:
changed
lib/gitlab/ci/pipeline/chain/command.rb
View file @
2eb74fb7
...
...
@@ -33,6 +33,13 @@ module Gitlab
end
end
def
merge_request_ref_exists?
strong_memoize
(
:merge_request_ref_exists
)
do
MergeRequest
.
merge_request_ref?
(
origin_ref
)
&&
project
.
repository
.
ref_exists?
(
origin_ref
)
end
end
def
ref
strong_memoize
(
:ref
)
do
Gitlab
::
Git
.
ref_name
(
origin_ref
)
...
...
lib/gitlab/ci/pipeline/chain/validate/abilities.rb
View file @
2eb74fb7
...
...
@@ -44,6 +44,8 @@ module Gitlab
access
.
can_update_branch?
(
@command
.
ref
)
elsif
@command
.
tag_exists?
access
.
can_create_tag?
(
@command
.
ref
)
elsif
@command
.
merge_request_ref_exists?
access
.
can_update_branch?
(
@command
.
merge_request
.
source_branch
)
else
true
# Allow it for now and we'll reject when we check ref existence
end
...
...
lib/gitlab/ci/pipeline/chain/validate/repository.rb
View file @
2eb74fb7
...
...
@@ -9,7 +9,7 @@ module Gitlab
include
Chain
::
Helpers
def
perform!
unless
@command
.
branch_exists?
||
@command
.
tag_exists?
unless
@command
.
branch_exists?
||
@command
.
tag_exists?
||
@command
.
merge_request_ref_exists?
return
error
(
'Reference not found'
)
end
...
...
spec/factories/merge_requests.rb
View file @
2eb74fb7
...
...
@@ -117,9 +117,20 @@ FactoryBot.define do
end
end
trait
:with_legacy_detached_merge_request_pipeline
do
after
(
:create
)
do
|
merge_request
|
merge_request
.
merge_request_pipelines
<<
create
(
:ci_pipeline
,
source: :merge_request_event
,
merge_request:
merge_request
,
project:
merge_request
.
source_project
,
ref:
merge_request
.
source_branch
,
sha:
merge_request
.
source_branch_sha
)
end
end
trait
:with_detached_merge_request_pipeline
do
after
(
:
build
)
do
|
merge_request
|
merge_request
.
merge_request_pipelines
<<
build
(
:ci_pipeline
,
after
(
:
create
)
do
|
merge_request
|
merge_request
.
merge_request_pipelines
<<
create
(
:ci_pipeline
,
source: :merge_request_event
,
merge_request:
merge_request
,
project:
merge_request
.
source_project
,
...
...
@@ -135,7 +146,7 @@ FactoryBot.define do
target_sha
{
target_branch_sha
}
end
after
(
:
build
)
do
|
merge_request
,
evaluator
|
after
(
:
create
)
do
|
merge_request
,
evaluator
|
merge_request
.
merge_request_pipelines
<<
create
(
:ci_pipeline
,
source: :merge_request_event
,
merge_request:
merge_request
,
...
...
spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
View file @
2eb74fb7
...
...
@@ -48,6 +48,24 @@ describe Gitlab::Ci::Pipeline::Chain::Command do
end
end
describe
'#merge_request_ref_exists?'
do
subject
{
command
.
merge_request_ref_exists?
}
let!
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
,
target_project:
project
)
}
context
'for existing merge request ref'
do
let
(
:origin_ref
)
{
merge_request
.
ref_path
}
it
{
is_expected
.
to
eq
(
true
)
}
end
context
'for branch ref'
do
let
(
:origin_ref
)
{
merge_request
.
source_branch
}
it
{
is_expected
.
to
eq
(
false
)
}
end
end
describe
'#ref'
do
subject
{
command
.
ref
}
...
...
spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
View file @
2eb74fb7
...
...
@@ -10,12 +10,33 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
let
(
:command
)
do
Gitlab
::
Ci
::
Pipeline
::
Chain
::
Command
.
new
(
project:
project
,
current_user:
user
,
origin_ref:
ref
)
project:
project
,
current_user:
user
,
origin_ref:
origin_ref
,
merge_request:
merge_request
)
end
let
(
:step
)
{
described_class
.
new
(
pipeline
,
command
)
}
let
(
:ref
)
{
'master'
}
let
(
:origin_ref
)
{
ref
}
let
(
:merge_request
)
{
nil
}
shared_context
'detached merge request pipeline'
do
let
(
:merge_request
)
do
create
(
:merge_request
,
source_project:
project
,
source_branch:
ref
,
target_project:
project
,
target_branch:
'feature'
)
end
let
(
:pipeline
)
do
build
(
:ci_pipeline
,
source: :merge_request_event
,
merge_request:
merge_request
,
project:
project
)
end
let
(
:origin_ref
)
{
merge_request
.
ref_path
}
end
context
'when users has no ability to run a pipeline'
do
before
do
...
...
@@ -58,6 +79,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
it
{
is_expected
.
to
be_truthy
}
context
'when pipeline is a detached merge request pipeline'
do
include_context
'detached merge request pipeline'
it
{
is_expected
.
to
be_truthy
}
end
context
'when the branch is protected'
do
let!
(
:protected_branch
)
do
create
(
:protected_branch
,
project:
project
,
name:
ref
)
...
...
@@ -65,6 +92,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
it
{
is_expected
.
to
be_falsey
}
context
'when pipeline is a detached merge request pipeline'
do
include_context
'detached merge request pipeline'
it
{
is_expected
.
to
be_falsey
}
end
context
'when developers are allowed to merge'
do
let!
(
:protected_branch
)
do
create
(
:protected_branch
,
...
...
@@ -74,6 +107,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
it
{
is_expected
.
to
be_truthy
}
context
'when pipeline is a detached merge request pipeline'
do
include_context
'detached merge request pipeline'
it
{
is_expected
.
to
be_truthy
}
end
end
end
...
...
@@ -112,6 +151,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
it
{
is_expected
.
to
be_truthy
}
context
'when pipeline is a detached merge request pipeline'
do
include_context
'detached merge request pipeline'
it
{
is_expected
.
to
be_truthy
}
end
end
context
'when the tag is protected'
do
...
...
spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
View file @
2eb74fb7
...
...
@@ -42,6 +42,25 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
context
'when origin ref is a merge request ref'
do
let
(
:command
)
do
Gitlab
::
Ci
::
Pipeline
::
Chain
::
Command
.
new
(
project:
project
,
current_user:
user
,
origin_ref:
origin_ref
,
checkout_sha:
checkout_sha
)
end
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
,
target_project:
project
)
}
let
(
:origin_ref
)
{
merge_request
.
ref_path
}
let
(
:checkout_sha
)
{
project
.
repository
.
commit
(
merge_request
.
ref_path
).
id
}
it
'does not break the chain'
do
expect
(
step
.
break?
).
to
be
false
end
it
'does not append pipeline errors'
do
expect
(
pipeline
.
errors
).
to
be_empty
end
end
context
'when ref is ambiguous'
do
let
(
:project
)
do
create
(
:project
,
:repository
).
tap
do
|
proj
|
...
...
spec/migrations/migrate_cluster_configure_worker_sidekiq_queue_spec.rb
View file @
2eb74fb7
...
...
@@ -3,12 +3,13 @@ require Rails.root.join('db', 'post_migrate', '20181219145520_migrate_cluster_co
describe
MigrateClusterConfigureWorkerSidekiqQueue
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queue'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_platform_configure'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_configure'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_platform_configure'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_configure'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -19,12 +20,12 @@ describe MigrateClusterConfigureWorkerSidekiqQueue, :sidekiq, :redis do
it
'does not affect other queues under the same namespace'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_install_app'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_provision'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_wait_for_app_installation'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:wait_for_cluster_creation'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_wait_for_ingress_ip_address'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_project_configure'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_install_app'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_provision'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_wait_for_app_installation'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:wait_for_cluster_creation'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_wait_for_ingress_ip_address'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_project_configure'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -39,7 +40,7 @@ describe MigrateClusterConfigureWorkerSidekiqQueue, :sidekiq, :redis do
it
'correctly migrates queue when migrating down'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'gcp_cluster:cluster_configure'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'gcp_cluster:cluster_configure'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
down
...
...
@@ -58,11 +59,4 @@ describe MigrateClusterConfigureWorkerSidekiqQueue, :sidekiq, :redis do
expect
{
described_class
.
new
.
down
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/migrations/migrate_create_trace_artifact_sidekiq_queue_spec.rb
View file @
2eb74fb7
...
...
@@ -3,12 +3,13 @@ require Rails.root.join('db', 'post_migrate', '20180306074045_migrate_create_tra
describe
MigrateCreateTraceArtifactSidekiqQueue
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queues'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_default:create_trace_artifact'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_background:archive_trace'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:create_trace_artifact'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_background:archive_trace'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -19,11 +20,11 @@ describe MigrateCreateTraceArtifactSidekiqQueue, :sidekiq, :redis do
it
'does not affect other queues under the same namespace'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_default:build_coverage'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:build_trace_sections'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:pipeline_metrics'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:pipeline_notification'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:build_coverage'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:build_trace_sections'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:pipeline_metrics'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:pipeline_notification'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -37,7 +38,7 @@ describe MigrateCreateTraceArtifactSidekiqQueue, :sidekiq, :redis do
it
'correctly migrates queue when migrating down'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_background:archive_trace'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_background:archive_trace'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
down
...
...
@@ -56,11 +57,4 @@ describe MigrateCreateTraceArtifactSidekiqQueue, :sidekiq, :redis do
expect
{
described_class
.
new
.
down
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/migrations/migrate_object_storage_upload_sidekiq_queue_spec.rb
View file @
2eb74fb7
...
...
@@ -3,12 +3,13 @@ require Rails.root.join('db', 'post_migrate', '20180603190921_migrate_object_sto
describe
MigrateObjectStorageUploadSidekiqQueue
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queue'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'object_storage_upload'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'object_storage:object_storage_background_move'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'object_storage_upload'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'object_storage:object_storage_background_move'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -23,11 +24,4 @@ describe MigrateObjectStorageUploadSidekiqQueue, :sidekiq, :redis do
expect
{
described_class
.
new
.
up
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb
View file @
2eb74fb7
...
...
@@ -3,12 +3,13 @@ require Rails.root.join('db', 'post_migrate', '20170822101017_migrate_pipeline_s
describe
MigratePipelineSidekiqQueues
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queues'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue: :pipeline
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue: :build
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue: :pipeline
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue: :build
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -20,10 +21,10 @@ describe MigratePipelineSidekiqQueues, :sidekiq, :redis do
it
'correctly migrates queue when migrating down'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue: :pipeline_default
).
perform_async
(
'Class'
,
[
1
])
stub
bed
_worker
(
queue: :pipeline_processing
).
perform_async
(
'Class'
,
[
2
])
stub
bed
_worker
(
queue: :pipeline_hooks
).
perform_async
(
'Class'
,
[
3
])
stub
bed
_worker
(
queue: :pipeline_cache
).
perform_async
(
'Class'
,
[
4
])
stub_worker
(
queue: :pipeline_default
).
perform_async
(
'Class'
,
[
1
])
stub_worker
(
queue: :pipeline_processing
).
perform_async
(
'Class'
,
[
2
])
stub_worker
(
queue: :pipeline_hooks
).
perform_async
(
'Class'
,
[
3
])
stub_worker
(
queue: :pipeline_cache
).
perform_async
(
'Class'
,
[
4
])
described_class
.
new
.
down
...
...
@@ -45,11 +46,4 @@ describe MigratePipelineSidekiqQueues, :sidekiq, :redis do
expect
{
described_class
.
new
.
down
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/migrations/migrate_storage_migrator_sidekiq_queue_spec.rb
View file @
2eb74fb7
...
...
@@ -3,11 +3,12 @@ require Rails.root.join('db', 'post_migrate', '20190124200344_migrate_storage_mi
describe
MigrateStorageMigratorSidekiqQueue
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queues'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue: :storage_migrator
).
perform_async
(
1
,
5
)
stub_worker
(
queue: :storage_migrator
).
perform_async
(
1
,
5
)
described_class
.
new
.
up
...
...
@@ -18,7 +19,7 @@ describe MigrateStorageMigratorSidekiqQueue, :sidekiq, :redis do
it
'correctly migrates queue when migrating down'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue: :'hashed_storage:hashed_storage_migrator'
).
perform_async
(
1
,
5
)
stub_worker
(
queue: :'hashed_storage:hashed_storage_migrator'
).
perform_async
(
1
,
5
)
described_class
.
new
.
down
...
...
@@ -37,11 +38,4 @@ describe MigrateStorageMigratorSidekiqQueue, :sidekiq, :redis do
expect
{
described_class
.
new
.
down
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/migrations/migrate_update_head_pipeline_for_merge_request_sidekiq_queue_spec.rb
View file @
2eb74fb7
...
...
@@ -3,12 +3,13 @@ require Rails.root.join('db', 'post_migrate', '20180307012445_migrate_update_hea
describe
MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue
,
:sidekiq
,
:redis
do
include
Gitlab
::
Database
::
MigrationHelpers
include
StubWorker
context
'when there are jobs in the queues'
do
it
'correctly migrates queue when migrating up'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_default:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_processing:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_processing:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -19,10 +20,10 @@ describe MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue, :sidekiq, :redis
it
'does not affect other queues under the same namespace'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_default:build_coverage'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:build_trace_sections'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:pipeline_metrics'
).
perform_async
(
'Something'
,
[
1
])
stub
bed
_worker
(
queue:
'pipeline_default:pipeline_notification'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:build_coverage'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:build_trace_sections'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:pipeline_metrics'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_default:pipeline_notification'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
up
...
...
@@ -35,7 +36,7 @@ describe MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue, :sidekiq, :redis
it
'correctly migrates queue when migrating down'
do
Sidekiq
::
Testing
.
disable!
do
stub
bed
_worker
(
queue:
'pipeline_processing:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
stub_worker
(
queue:
'pipeline_processing:update_head_pipeline_for_merge_request'
).
perform_async
(
'Something'
,
[
1
])
described_class
.
new
.
down
...
...
@@ -54,11 +55,4 @@ describe MigrateUpdateHeadPipelineForMergeRequestSidekiqQueue, :sidekiq, :redis
expect
{
described_class
.
new
.
down
}.
not_to
raise_error
end
end
def
stubbed_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
spec/models/ci/build_spec.rb
View file @
2eb74fb7
...
...
@@ -25,6 +25,8 @@ describe Ci::Build do
it
{
is_expected
.
to
respond_to
(
:has_trace?
)
}
it
{
is_expected
.
to
respond_to
(
:trace
)
}
it
{
is_expected
.
to
delegate_method
(
:merge_request_event?
).
to
(
:pipeline
)
}
it
{
is_expected
.
to
delegate_method
(
:merge_request_ref?
).
to
(
:pipeline
)
}
it
{
is_expected
.
to
delegate_method
(
:legacy_detached_merge_request_pipeline?
).
to
(
:pipeline
)
}
it
{
is_expected
.
to
be_a
(
ArtifactMigratable
)
}
...
...
@@ -3627,6 +3629,24 @@ describe Ci::Build do
it
{
is_expected
.
to
be_falsey
}
end
end
context
'when refspecs feature is required by build'
do
before
do
allow
(
build
).
to
receive
(
:merge_request_ref?
)
{
true
}
end
context
'when runner provides given feature'
do
let
(
:runner_features
)
{
{
refspecs:
true
}
}
it
{
is_expected
.
to
be_truthy
}
end
context
'when runner does not provide given feature'
do
let
(
:runner_features
)
{
{}
}
it
{
is_expected
.
to
be_falsey
}
end
end
end
describe
'#deployment_status'
do
...
...
spec/models/ci/pipeline_spec.rb
View file @
2eb74fb7
...
...
@@ -366,6 +366,42 @@ describe Ci::Pipeline, :mailer do
end
end
describe
'#merge_request_ref?'
do
subject
{
pipeline
.
merge_request_ref?
}
it
'calls MergeRequest#merge_request_ref?'
do
expect
(
MergeRequest
).
to
receive
(
:merge_request_ref?
).
with
(
pipeline
.
ref
)
subject
end
end
describe
'#legacy_detached_merge_request_pipeline?'
do
subject
{
pipeline
.
legacy_detached_merge_request_pipeline?
}
set
(
:merge_request
)
{
create
(
:merge_request
)
}
let
(
:ref
)
{
'feature'
}
let
(
:target_sha
)
{
nil
}
let
(
:pipeline
)
do
build
(
:ci_pipeline
,
source: :merge_request_event
,
merge_request:
merge_request
,
ref:
ref
,
target_sha:
target_sha
)
end
it
{
is_expected
.
to
be_truthy
}
context
'when pipeline ref is a merge request ref'
do
let
(
:ref
)
{
'refs/merge-requests/1/head'
}
it
{
is_expected
.
to
be_falsy
}
end
context
'when target sha is set'
do
let
(
:target_sha
)
{
'target-sha'
}
it
{
is_expected
.
to
be_falsy
}
end
end
describe
'#matches_sha_or_source_sha?'
do
subject
{
pipeline
.
matches_sha_or_source_sha?
(
sample_sha
)
}
...
...
spec/models/merge_request_spec.rb
View file @
2eb74fb7
...
...
@@ -3114,4 +3114,32 @@ describe MergeRequest do
end
end
end
describe
'.merge_request_ref?'
do
subject
{
described_class
.
merge_request_ref?
(
ref
)
}
context
'when ref is ref name of a branch'
do
let
(
:ref
)
{
'feature'
}
it
{
is_expected
.
to
be_falsey
}
end
context
'when ref is HEAD ref path of a branch'
do
let
(
:ref
)
{
'refs/heads/feature'
}
it
{
is_expected
.
to
be_falsey
}
end
context
'when ref is HEAD ref path of a merge request'
do
let
(
:ref
)
{
'refs/merge-requests/1/head'
}
it
{
is_expected
.
to
be_truthy
}
end
context
'when ref is merge ref path of a merge request'
do
let
(
:ref
)
{
'refs/merge-requests/1/merge'
}
it
{
is_expected
.
to
be_truthy
}
end
end
end
spec/presenters/ci/build_runner_presenter_spec.rb
View file @
2eb74fb7
...
...
@@ -136,6 +136,24 @@ describe Ci::BuildRunnerPresenter do
is_expected
.
to
eq
(
1
)
end
end
context
'when pipeline is detached merge request pipeline'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
:with_detached_merge_request_pipeline
)
}
let
(
:pipeline
)
{
merge_request
.
all_pipelines
.
first
}
let
(
:build
)
{
create
(
:ci_build
,
ref:
pipeline
.
ref
,
pipeline:
pipeline
)
}
it
'returns the default git depth for pipelines for merge requests'
do
is_expected
.
to
eq
(
described_class
::
DEFAULT_GIT_DEPTH_MERGE_REQUEST
)
end
context
'when pipeline is legacy detached merge request pipeline'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
:with_legacy_detached_merge_request_pipeline
)
}
it
'behaves as branch pipeline'
do
is_expected
.
to
eq
(
0
)
end
end
end
end
describe
'#refspecs'
do
...
...
@@ -165,5 +183,25 @@ describe Ci::BuildRunnerPresenter do
end
end
end
context
'when pipeline is detached merge request pipeline'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
:with_detached_merge_request_pipeline
)
}
let
(
:pipeline
)
{
merge_request
.
all_pipelines
.
first
}
let
(
:build
)
{
create
(
:ci_build
,
ref:
pipeline
.
ref
,
pipeline:
pipeline
)
}
it
'returns the correct refspecs'
do
is_expected
.
to
contain_exactly
(
'+refs/merge-requests/1/head:refs/merge-requests/1/head'
)
end
context
'when pipeline is legacy detached merge request pipeline'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
:with_legacy_detached_merge_request_pipeline
)
}
it
'returns the correct refspecs'
do
is_expected
.
to
contain_exactly
(
'+refs/tags/*:refs/tags/*'
,
'+refs/heads/*:refs/remotes/origin/*'
)
end
end
end
end
end
spec/services/merge_requests/create_service_spec.rb
View file @
2eb74fb7
...
...
@@ -173,7 +173,7 @@ describe MergeRequests::CreateService do
end
end
describe
'
Merge request pipeline
s'
do
describe
'
Pipelines for merge request
s'
do
before
do
stub_ci_pipeline_yaml_file
(
YAML
.
dump
(
config
))
end
...
...
@@ -189,12 +189,46 @@ describe MergeRequests::CreateService do
}
end
it
'creates a merge request pipeline and sets it as a head pipeline'
do
it
'creates a
detached
merge request pipeline and sets it as a head pipeline'
do
expect
(
merge_request
).
to
be_persisted
merge_request
.
reload
expect
(
merge_request
.
merge_request_pipelines
.
count
).
to
eq
(
1
)
expect
(
merge_request
.
actual_head_pipeline
).
to
be_merge_request_event
expect
(
merge_request
.
actual_head_pipeline
).
to
be_detached_merge_request_pipeline
end
context
'when merge request is submitted from forked project'
do
let
(
:target_project
)
{
fork_project
(
project
,
nil
,
repository:
true
)
}
let
(
:opts
)
do
{
title:
'Awesome merge_request'
,
source_branch:
'feature'
,
target_branch:
'master'
,
target_project_id:
target_project
.
id
}
end
before
do
target_project
.
add_developer
(
assignee
)
target_project
.
add_maintainer
(
user
)
end
it
'create legacy detached merge request pipeline for fork merge request'
do
expect
(
merge_request
.
actual_head_pipeline
)
.
to
be_legacy_detached_merge_request_pipeline
end
end
context
'when ci_use_merge_request_ref feature flag is false'
do
before
do
stub_feature_flags
(
ci_use_merge_request_ref:
false
)
end
it
'create legacy detached merge request pipeline for non-fork merge request'
do
expect
(
merge_request
.
actual_head_pipeline
)
.
to
be_legacy_detached_merge_request_pipeline
end
end
context
'when there are no commits between source branch and target branch'
do
...
...
@@ -207,7 +241,7 @@ describe MergeRequests::CreateService do
}
end
it
'does not create a merge request pipeline'
do
it
'does not create a
detached
merge request pipeline'
do
expect
(
merge_request
).
to
be_persisted
merge_request
.
reload
...
...
@@ -225,7 +259,7 @@ describe MergeRequests::CreateService do
merge_request
end
it
'sets the latest merge request pipeline as the head pipeline'
do
it
'sets the latest
detached
merge request pipeline as the head pipeline'
do
expect
(
merge_request
.
actual_head_pipeline
).
to
be_merge_request_event
end
end
...
...
@@ -235,7 +269,7 @@ describe MergeRequests::CreateService do
stub_feature_flags
(
ci_merge_request_pipeline:
false
)
end
it
'does not create a merge request pipeline'
do
it
'does not create a
detached
merge request pipeline'
do
expect
(
merge_request
).
to
be_persisted
merge_request
.
reload
...
...
@@ -254,7 +288,7 @@ describe MergeRequests::CreateService do
}
end
it
'does not create a merge request pipeline'
do
it
'does not create a
detached
merge request pipeline'
do
expect
(
merge_request
).
to
be_persisted
merge_request
.
reload
...
...
spec/services/merge_requests/refresh_service_spec.rb
View file @
2eb74fb7
...
...
@@ -147,7 +147,7 @@ describe MergeRequests::RefreshService do
end
end
describe
'
Merge request pipeline
s'
do
describe
'
Pipelines for merge request
s'
do
before
do
stub_ci_pipeline_yaml_file
(
YAML
.
dump
(
config
))
end
...
...
@@ -165,7 +165,7 @@ describe MergeRequests::RefreshService do
}
end
it
'create merge request pipeline with commits'
do
it
'create
detached
merge request pipeline with commits'
do
expect
{
subject
}
.
to
change
{
@merge_request
.
merge_request_pipelines
.
count
}.
by
(
1
)
.
and
change
{
@fork_merge_request
.
merge_request_pipelines
.
count
}.
by
(
1
)
...
...
@@ -176,7 +176,34 @@ describe MergeRequests::RefreshService do
expect
(
@another_merge_request
.
has_commits?
).
to
be_falsy
end
context
"when branch pipeline was created before a merge request pipline has been created"
do
it
'create detached merge request pipeline for non-fork merge request'
do
subject
expect
(
@merge_request
.
merge_request_pipelines
.
first
)
.
to
be_detached_merge_request_pipeline
end
it
'create legacy detached merge request pipeline for fork merge request'
do
subject
expect
(
@fork_merge_request
.
merge_request_pipelines
.
first
)
.
to
be_legacy_detached_merge_request_pipeline
end
context
'when ci_use_merge_request_ref feature flag is false'
do
before
do
stub_feature_flags
(
ci_use_merge_request_ref:
false
)
end
it
'create legacy detached merge request pipeline for non-fork merge request'
do
subject
expect
(
@merge_request
.
merge_request_pipelines
.
first
)
.
to
be_legacy_detached_merge_request_pipeline
end
end
context
"when branch pipeline was created before a detaced merge request pipeline has been created"
do
before
do
create
(
:ci_pipeline
,
project:
@merge_request
.
source_project
,
sha:
@merge_request
.
diff_head_sha
,
...
...
@@ -186,7 +213,7 @@ describe MergeRequests::RefreshService do
subject
end
it
'sets the latest merge request pipeline as a head pipeline'
do
it
'sets the latest
detached
merge request pipeline as a head pipeline'
do
@merge_request
.
reload
expect
(
@merge_request
.
actual_head_pipeline
).
to
be_merge_request_event
end
...
...
@@ -199,7 +226,7 @@ describe MergeRequests::RefreshService do
end
context
"when MergeRequestUpdateWorker is retried by an exception"
do
it
'does not re-create a duplicate merge request pipeline'
do
it
'does not re-create a duplicate
detached
merge request pipeline'
do
expect
do
service
.
new
(
@project
,
@user
).
execute
(
@oldrev
,
@newrev
,
'refs/heads/master'
)
end
.
to
change
{
@merge_request
.
merge_request_pipelines
.
count
}.
by
(
1
)
...
...
@@ -215,7 +242,7 @@ describe MergeRequests::RefreshService do
stub_feature_flags
(
ci_merge_request_pipeline:
false
)
end
it
'does not create a merge request pipeline'
do
it
'does not create a
detached
merge request pipeline'
do
expect
{
subject
}
.
not_to
change
{
@merge_request
.
merge_request_pipelines
.
count
}
end
...
...
@@ -232,7 +259,7 @@ describe MergeRequests::RefreshService do
}
end
it
'does not create a merge request pipeline'
do
it
'does not create a
detached
merge request pipeline'
do
expect
{
subject
}
.
not_to
change
{
@merge_request
.
merge_request_pipelines
.
count
}
end
...
...
spec/support/helpers/stub_worker.rb
0 → 100644
View file @
2eb74fb7
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module
StubWorker
def
stub_worker
(
queue
:)
Class
.
new
do
include
Sidekiq
::
Worker
sidekiq_options
queue:
queue
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment