Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-26 21:10:49 +00:00
parent 4c47bc5ec6
commit 84d72a5660
64 changed files with 493 additions and 485 deletions

View File

@ -121,6 +121,7 @@ review-stop:
QA_ARTIFACTS_DIR: "${CI_PROJECT_DIR}/qa"
QA_CAN_TEST_GIT_PROTOCOL_V2: "false"
QA_DEBUG: "true"
QA_GENERATE_ALLURE_REPORT: "true"
GITLAB_USERNAME: "root"
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
GITLAB_ADMIN_USERNAME: "root"
@ -141,6 +142,23 @@ review-stop:
expire_in: 7 days
when: always
.allure-report-base:
image:
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.0.6
entrypoint: [""]
stage: post-qa
variables:
GIT_STRATEGY: none
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
script:
- |
allure-report-publisher upload gcs \
--results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \
--bucket="gitlab-qa-allure-reports" \
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
--copy-latest \
--color
review-qa-smoke:
extends:
- .review-qa-base
@ -210,6 +228,22 @@ parallel-spec-reports:
junit: qa/gitlab-qa-run-*/**/rspec-*.xml
expire_in: 31d
allure-report-qa-smoke:
extends:
- .allure-report-base
- .review:rules:review-qa-smoke
needs: ["review-qa-smoke"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
allure-report-qa-all:
extends:
- .allure-report-base
- .review:rules:review-qa-all
needs: ["review-qa-all"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
danger-review:
extends:
- .default-retry

View File

@ -1309,7 +1309,6 @@ RSpec/AnyInstanceOf:
- 'spec/support/shared_examples/workers/authorized_projects_worker_shared_example.rb'
- 'spec/support/shared_examples/workers/reactive_cacheable_shared_examples.rb'
- 'spec/support/snowplow.rb'
- 'spec/support/unicorn.rb'
- 'spec/tasks/gitlab/cleanup_rake_spec.rb'
- 'spec/tasks/gitlab/container_registry_rake_spec.rb'
- 'spec/tasks/gitlab/db_rake_spec.rb'

View File

@ -305,7 +305,7 @@ gem 'gitlab-license', '~> 1.5'
gem 'rack-attack', '~> 6.3.0'
# Sentry integration
gem 'sentry-raven', '~> 3.0'
gem 'sentry-raven', '~> 3.1'
# PostgreSQL query parsing
gem 'pg_query', '~> 2.0.3'

View File

@ -1170,7 +1170,7 @@ GEM
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
sentry-raven (3.0.4)
sentry-raven (3.1.2)
faraday (>= 1.0)
settingslogic (2.0.9)
sexp_processor (4.15.1)
@ -1620,7 +1620,7 @@ DEPENDENCIES
sassc-rails (~> 2.1.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 3.142)
sentry-raven (~> 3.0)
sentry-raven (~> 3.1)
settingslogic (~> 2.0.9)
shoulda-matchers (~> 4.0.1)
sidekiq (~> 5.2.7)

View File

@ -79,13 +79,20 @@ export default {
<jobs-table-tabs @fetchJobsByStatus="fetchJobsByStatus" />
<div v-if="$apollo.loading" class="gl-mt-5">
<gl-skeleton-loader
preserve-aspect-ratio="none"
equal-width-lines
:lines="5"
:width="600"
:height="66"
/>
<gl-skeleton-loader :width="1248" :height="73">
<circle cx="748.031" cy="37.7193" r="15.0307" />
<circle cx="787.241" cy="37.7193" r="15.0307" />
<circle cx="827.759" cy="37.7193" r="15.0307" />
<circle cx="866.969" cy="37.7193" r="15.0307" />
<circle cx="380" cy="37" r="18" />
<rect x="432" y="19" width="126.587" height="15" />
<rect x="432" y="41" width="247" height="15" />
<rect x="158" y="19" width="86.1" height="15" />
<rect x="158" y="41" width="168" height="15" />
<rect x="22" y="19" width="96" height="36" />
<rect x="924" y="30" width="96" height="15" />
<rect x="1057" y="20" width="166" height="35" />
</gl-skeleton-loader>
</div>
<jobs-table-empty-state v-else-if="showEmptyState" />

View File

@ -50,7 +50,7 @@ class IssuableFinder
attr_reader :original_params
attr_writer :parent
delegate(*%i[assignee milestones], to: :params)
delegate(*%i[milestones], to: :params)
class << self
def scalar_params
@ -148,7 +148,6 @@ class IssuableFinder
# Negates all params found in `negatable_params`
def filter_negated_items(items)
items = by_negated_assignee(items)
items = by_negated_label(items)
items = by_negated_milestone(items)
items = by_negated_release(items)
@ -365,32 +364,21 @@ class IssuableFinder
def by_author(items)
Issuables::AuthorFilter.new(
items,
params: original_params,
or_filters_enabled: or_filters_enabled?
).filter
).filter(items)
end
def by_assignee(items)
if params.filter_by_no_assignee?
items.unassigned
elsif params.filter_by_any_assignee?
items.assigned
elsif params.assignee
items.assigned_to(params.assignee)
elsif params.assignee_id? || params.assignee_username? # assignee not found
items.none
else
items
end
assignee_filter.filter(items)
end
def by_negated_assignee(items)
# We want CE users to be able to say "Issues not assigned to either PersonA nor PersonB"
if not_params.assignees.present?
items.not_assigned_to(not_params.assignees)
else
items
def assignee_filter
strong_memoize(:assignee_filter) do
Issuables::AssigneeFilter.new(
params: original_params,
or_filters_enabled: or_filters_enabled?
)
end
end

View File

@ -27,14 +27,6 @@ class IssuableFinder
params.present?
end
def filter_by_no_assignee?
params[:assignee_id].to_s.downcase == FILTER_NONE
end
def filter_by_any_assignee?
params[:assignee_id].to_s.downcase == FILTER_ANY
end
def filter_by_no_label?
downcased = label_names.map(&:downcase)
@ -156,24 +148,6 @@ class IssuableFinder
end
end
# rubocop: disable CodeReuse/ActiveRecord
def assignees
strong_memoize(:assignees) do
if assignee_id?
User.where(id: params[:assignee_id])
elsif assignee_username?
User.where(username: params[:assignee_username])
else
User.none
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def assignee
assignees.first
end
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
module Issuables
class AssigneeFilter < BaseFilter
def filter(issuables)
filtered = by_assignee(issuables)
filtered = by_assignee_union(filtered)
by_negated_assignee(filtered)
end
def includes_user?(user)
Array(params[:assignee_ids]).include?(user.id) ||
Array(params[:assignee_id]).include?(user.id) ||
Array(params[:assignee_username]).include?(user.username)
end
private
def by_assignee(issuables)
if filter_by_no_assignee?
issuables.unassigned
elsif filter_by_any_assignee?
issuables.assigned
elsif has_assignee_param?(params)
filter_by_assignees(issuables)
else
issuables
end
end
def by_assignee_union(issuables)
return issuables unless or_filters_enabled? && has_assignee_param?(or_params)
issuables.assigned_to(assignee_ids(or_params))
end
def by_negated_assignee(issuables)
return issuables unless has_assignee_param?(not_params)
issuables.not_assigned_to(assignee_ids(not_params))
end
def filter_by_no_assignee?
params[:assignee_id].to_s.downcase == FILTER_NONE
end
def filter_by_any_assignee?
params[:assignee_id].to_s.downcase == FILTER_ANY
end
def filter_by_assignees(issuables)
assignee_ids = assignee_ids(params)
return issuables.none if assignee_ids.blank?
assignee_ids.each do |assignee_id|
issuables = issuables.assigned_to(assignee_id)
end
issuables
end
def has_assignee_param?(specific_params)
return if specific_params.nil?
specific_params[:assignee_ids].present? || specific_params[:assignee_id].present? || specific_params[:assignee_username].present?
end
def assignee_ids(specific_params)
if specific_params[:assignee_ids].present?
Array(specific_params[:assignee_ids])
elsif specific_params[:assignee_id].present?
Array(specific_params[:assignee_id])
elsif specific_params[:assignee_username].present?
User.by_username(specific_params[:assignee_username]).select(:id)
end
end
end
end

View File

@ -2,7 +2,7 @@
module Issuables
class AuthorFilter < BaseFilter
def filter
def filter(issuables)
filtered = by_author(issuables)
filtered = by_author_union(filtered)
by_negated_author(filtered)
@ -21,7 +21,7 @@ module Issuables
end
def by_author_union(issuables)
return issuables unless or_filters_enabled? && or_params&.fetch(:author_username).present?
return issuables unless or_filters_enabled? && or_params&.fetch(:author_username, false).present?
issuables.authored(User.by_username(or_params[:author_username]))
end

View File

@ -2,10 +2,12 @@
module Issuables
class BaseFilter
attr_reader :issuables, :params
attr_reader :params
def initialize(issuables, params:, or_filters_enabled: false)
@issuables = issuables
FILTER_NONE = 'none'
FILTER_ANY = 'any'
def initialize(params:, or_filters_enabled: false)
@params = params
@or_filters_enabled = or_filters_enabled
end

View File

@ -52,7 +52,7 @@ class IssuesFinder < IssuableFinder
# can always see confidential issues assigned to them. This is just an
# optimization since a very common usecase of this Finder is to load the
# count of issues assigned to the user for the header bar.
return Issue.all if current_user && params.assignees.include?(current_user)
return Issue.all if current_user && assignee_filter.includes_user?(current_user)
return Issue.where('issues.confidential IS NOT TRUE') if params.user_cannot_see_confidential_issues?

View File

@ -200,7 +200,7 @@ module IssuesHelper
jira_integration_path: help_page_url('integration/jira/', anchor: 'view-jira-issues'),
markdown_help_path: help_page_path('user/markdown'),
max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes),
new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.try(:id), milestone_id: finder.milestones.first.try(:id) }),
new_issue_path: new_project_issue_path(project, issue: { milestone_id: finder.milestones.first.try(:id) }),
project_import_jira_path: project_import_jira_path(project),
project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json),
project_milestones_path: project_milestones_path(project, format: :json),

View File

@ -163,6 +163,8 @@ module Ci
numericality: { greater_than_or_equal_to: 0.0,
message: 'needs to be non-negative' }
validates :config, json_schema: { filename: 'ci_runner_config' }
# Searches for runners matching the given query.
#
# This method uses ILIKE on PostgreSQL.
@ -353,7 +355,7 @@ module Ci
end
def heartbeat(values)
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address) || {}
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config) || {}
values[:contacted_at] = Time.current
cache_attributes(values)

View File

@ -101,21 +101,20 @@ module Issuable
scope :unassigned, -> do
where("NOT EXISTS (SELECT TRUE FROM #{to_ability_name}_assignees WHERE #{to_ability_name}_id = #{to_ability_name}s.id)")
end
scope :assigned_to, ->(u) do
assignees_table = Arel::Table.new("#{to_ability_name}_assignees")
sql = assignees_table.project('true').where(assignees_table[:user_id].in(u.id)).where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
where("EXISTS (#{sql.to_sql})")
scope :assigned_to, ->(users) do
assignees_class = self.reflect_on_association("#{to_ability_name}_assignees").klass
condition = assignees_class.where(user_id: users).where(Arel.sql("#{to_ability_name}_id = #{to_ability_name}s.id"))
where(condition.arel.exists)
end
scope :not_assigned_to, ->(users) do
assignees_class = self.reflect_on_association("#{to_ability_name}_assignees").klass
condition = assignees_class.where(user_id: users).where(Arel.sql("#{to_ability_name}_id = #{to_ability_name}s.id"))
where(condition.arel.exists.not)
end
# rubocop:enable GitlabSecurity/SqlInjection
scope :not_assigned_to, ->(users) do
assignees_table = Arel::Table.new("#{to_ability_name}_assignees")
sql = assignees_table.project('true')
.where(assignees_table[:user_id].in(users))
.where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
where(sql.exists.not)
end
scope :without_particular_labels, ->(label_names) do
labels_table = Label.arel_table
label_links_table = LabelLink.arel_table

View File

@ -8,7 +8,7 @@ class GroupMember < Member
belongs_to :group, foreign_key: 'source_id'
alias_attribute :namespace_id, :source_id
delegate :update_two_factor_requirement, to: :user
delegate :update_two_factor_requirement, to: :user, allow_nil: true
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE

View File

@ -37,7 +37,7 @@ class MergeRequest < ApplicationRecord
SORTING_PREFERENCE_FIELD = :merge_requests_sort
ALLOWED_TO_USE_MERGE_BASE_PIPELINE_FOR_COMPARISON = {
'Ci::CompareMetricsReportsService' => ->(project) { ::Gitlab::Ci::Features.merge_base_pipeline_for_metrics_comparison?(project) },
'Ci::CompareMetricsReportsService' => ->(project) { true },
'Ci::CompareCodequalityReportsService' => ->(project) { true }
}.freeze

View File

@ -86,7 +86,7 @@ module DesignManagement
def with_temporary_branch(&block)
target_repository.create_if_not_exists
create_master_branch! if target_repository.empty?
create_default_branch! if target_repository.empty?
create_temporary_branch!
yield
@ -95,9 +95,9 @@ module DesignManagement
end
# A project that does not have any designs will have a blank design
# repository. To create a temporary branch from `master` we need
# create `master` first by adding a file to it.
def create_master_branch!
# repository. To create a temporary branch from default branch we need to
# create default branch first by adding a file to it.
def create_default_branch!
target_repository.create_file(
git_user,
".CopyDesignCollectionService_#{Time.now.to_i}",
@ -121,7 +121,7 @@ module DesignManagement
target_repository.rm_branch(git_user, temporary_branch)
end
# Merge the temporary branch containing the commits to `master`
# Merge the temporary branch containing the commits to default branch
# and update the state of the target_design_collection.
def finalize!
source_sha = shas.last

View File

@ -13,7 +13,7 @@ module DesignManagement
attr_reader :issue
def target_branch
repository.root_ref || "master"
repository.root_ref || Gitlab::DefaultBranch.value(object: project)
end
def collection

View File

@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "CI Runner config values",
"type": "object",
"properties": {
"gpus": { "type": "string" }
},
"additionalProperties": false
}

View File

@ -32,7 +32,7 @@
%td{ style: "#{default_style}border-top:1px solid #ededed;" }
= _('Time')
%td{ style: "#{default_style}color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
= @time.strftime('%Y-%m-%d %l:%M:%S %p %Z')
= @time.strftime('%Y-%m-%d %k:%M:%S %Z')
%tr.spacer
%td{ style: spacer_style }
&nbsp;

View File

@ -83,7 +83,7 @@
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
= form_for @project, html: { class: 'new_project gl-show-field-errors' } do |f|
= form_for @project, html: { class: 'new_project' } do |f|
%hr
= render "shared/import_form", f: f
= render 'projects/new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label

View File

@ -15,8 +15,7 @@
= button_tag _("Edit issues"), class: "gl-button btn btn-default gl-mr-3 js-bulk-update-toggle"
- if show_new_issue_link?(@project)
= link_to _("New issue"), new_project_issue_path(@project,
issue: { assignee_id: finder.assignee.try(:id),
milestone_id: finder.milestones.first.try(:id) }),
issue: { milestone_id: finder.milestones.first.try(:id) }),
class: "gl-button btn btn-confirm",
id: "new_issue_link"

View File

@ -6,14 +6,8 @@
= f.label :import_url, class: 'label-bold' do
%span
= _('Git repository URL')
= f.text_field :import_url,
value: import_url.sanitized_url,
autocomplete: 'off',
class: 'form-control gl-form-input',
placeholder: 'https://gitlab.company.com/group/project.git',
required: true,
pattern: '(?:git|https?):\/\/.*/.*\.git$',
title: _('Please provide a valid URL ending with .git')
= f.text_field :import_url, value: import_url.sanitized_url,
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
.row
.form-group.col-md-6

View File

@ -1,8 +0,0 @@
---
name: merge_base_pipeline_for_metrics_comparison
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61282
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330809
milestone: '13.12'
type: development
group: group::testing
default_enabled: false

View File

@ -2,8 +2,8 @@
if ENV['ENABLE_RBTRACE']
Gitlab::Cluster::LifecycleEvents.on_worker_start do
# Unicorn clears out signals before it forks, so rbtrace won't work
# unless it is enabled after the fork.
# We need to require `rbtrace` in a context of a worker process.
# See https://github.com/tmm1/rbtrace/issues/56#issuecomment-648683596.
require 'rbtrace'
end
end

View File

@ -1,144 +0,0 @@
# Sample verbose configuration file for Unicorn (not Rack)
#
# This configuration file documents many features of Unicorn
# that may not be needed for some applications. See
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
# for a much simpler configuration file.
#
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
# Note: If you change this file in a merge request, please also create a
# merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
# Relative URL support
# WARNING: We recommend using an FQDN to host GitLab in a root path instead
# of using a relative URL.
# Documentation: http://doc.gitlab.com/ce/install/relative_url.html
# Uncomment and customize the following line to run in a non-root path
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
# Read about unicorn workers here:
# http://doc.gitlab.com/ee/install/requirements.html#unicorn-workers
#
worker_processes 3
# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
# as root unless it's from system init scripts.
# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
# user "unprivileged_user", "unprivileged_group"
# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
working_directory "/home/git/gitlab" # available in 0.94.0+
# Listen on both a Unix domain socket and a TCP port.
# If you are load-balancing multiple Unicorn masters, lower the backlog
# setting to e.g. 64 for faster failover.
listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 1024
listen "127.0.0.1:8080", :tcp_nopush => true
# destroy workers after 30 seconds instead of 60 seconds (the default)
#
# NOTICE: git push over http depends on this value.
# If you want to be able to push huge amount of data to git repository over http
# you will have to increase this value too.
#
# Example of output if you try to push 1GB repo to GitLab over http.
# -> git push http://gitlab.... master
#
# error: RPC failed; result=18, HTTP code = 200
# fatal: The remote end hung up unexpectedly
# fatal: The remote end hung up unexpectedly
#
# For more information see http://stackoverflow.com/a/21682112/752049
#
timeout 60
# feel free to point this anywhere accessible on the filesystem
pid "/home/git/gitlab/tmp/pids/unicorn.pid"
# By default, the Unicorn logger will write to stderr.
# Additionally, some applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
stderr_path "/home/git/gitlab/log/unicorn.stderr.log"
stdout_path "/home/git/gitlab/log/unicorn.stdout.log"
# Save memory by sharing the application code among multiple Unicorn workers
# with "preload_app true". See:
# https://www.rubydoc.info/gems/unicorn/5.1.0/Unicorn%2FConfigurator:preload_app
# https://brandur.org/ruby-memory#copy-on-write
preload_app true
# Enable this flag to have unicorn test client connections by writing the
# beginning of the HTTP headers before calling the application. This
# prevents calling the application for connections that have disconnected
# while queued. This is only guaranteed to detect clients on the same
# host unicorn runs on, and unlikely to detect disconnects even on a
# fast LAN.
check_client_connection false
require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
require_relative "/home/git/gitlab/lib/gitlab/log_timestamp_formatter.rb"
before_exec do |server|
# Signal application hooks that we're about to restart
Gitlab::Cluster::LifecycleEvents.do_before_master_restart
end
run_once = true
before_fork do |server, worker|
if run_once
# There is a difference between Puma and Unicorn:
# - Puma calls before_fork once when booting up master process
# - Unicorn runs before_fork whenever new work is spawned
# To unify this behavior we call before_fork only once (we use
# this callback for deleting Prometheus files so for our purposes
# it makes sense to align behavior with Puma)
run_once = false
# Signal application hooks that we're about to fork
Gitlab::Cluster::LifecycleEvents.do_before_fork
end
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
# twice as many worker_processes as you have configured.
#
# This allows a new master process to incrementally
# phase out the old master process with SIGTTOU to avoid a
# thundering herd (especially in the "preload_app false" case)
# when doing a transparent upgrade. The last worker spawned
# will then kill off the old master process with a SIGQUIT.
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
#
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
end
after_fork do |server, worker|
# Signal application hooks of worker start
Gitlab::Cluster::LifecycleEvents.do_worker_start
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
end
# Configure the default logger to use a custom formatter that formats the
# timestamps to be in UTC and in ISO8601.3 format
Configurator::DEFAULTS[:logger].formatter = Gitlab::LogTimestampFormatter.new

View File

@ -1,77 +0,0 @@
# frozen_string_literal: true
# -------------------------------------------------------------------------
# This file is used by the GDK to generate a default config/unicorn.rb file
# Note that `/home/git` will be substituted for the actual GDK root
# directory when this file is generated
# -------------------------------------------------------------------------
worker_processes 2
timeout 60
listen '/home/git/gitlab.socket'
preload_app true
check_client_connection false
require_relative "/home/git/gitlab/lib/gitlab/cluster/lifecycle_events"
require_relative "/home/git/gitlab/lib/gitlab/log_timestamp_formatter.rb"
before_exec do |server|
# Signal application hooks that we're about to restart
Gitlab::Cluster::LifecycleEvents.do_before_master_restart
end
run_once = true
before_fork do |server, worker|
if run_once
# There is a difference between Puma and Unicorn:
# - Puma calls before_fork once when booting up master process
# - Unicorn runs before_fork whenever new work is spawned
# To unify this behavior we call before_fork only once (we use
# this callback for deleting Prometheus files so for our purposes
# it makes sense to align behavior with Puma)
run_once = false
# Signal application hooks that we're about to fork
Gitlab::Cluster::LifecycleEvents.do_before_fork
end
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
# twice as many worker_processes as you have configured.
#
# This allows a new master process to incrementally
# phase out the old master process with SIGTTOU to avoid a
# thundering herd (especially in the "preload_app false" case)
# when doing a transparent upgrade. The last worker spawned
# will then kill off the old master process with a SIGQUIT.
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
#
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
end
after_fork do |server, worker|
# Signal application hooks of worker start
Gitlab::Cluster::LifecycleEvents.do_worker_start
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
end
# Configure the default logger to use a custom formatter that formats the
# timestamps to be in UTC and in ISO8601.3 format
Configurator::DEFAULTS[:logger].formatter = Gitlab::LogTimestampFormatter.new

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddConfigToCiRunners < ActiveRecord::Migration[6.0]
def change
add_column :ci_runners, :config, :jsonb, default: {}, null: false
end
end

View File

@ -0,0 +1 @@
0bd47f9055aab927a4e8efb4f995f44532880926af9892af60f7d2b8dcdef4a6

View File

@ -11148,7 +11148,8 @@ CREATE TABLE ci_runners (
runner_type smallint NOT NULL,
token_encrypted character varying,
public_projects_minutes_cost_factor double precision DEFAULT 0.0 NOT NULL,
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
config jsonb DEFAULT '{}'::jsonb NOT NULL
);
CREATE SEQUENCE ci_runners_id_seq

View File

@ -154,6 +154,8 @@ To change the namespace linked to a subscription:
Subscription charges are calculated based on the total number of users in a group, including its subgroups and nested projects. If the total number of users exceeds the number of seats in your subscription, your account is charged for the additional users.
Only one namespace can be linked to a subscription.
### Change Customers Portal account password
To change the password for this customers portal account:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -30,4 +30,4 @@ for a notification email to be sent.
## Example email
![Unknown sign in email](img/unknown_sign_in_email_v13_1.png)
![Unknown sign in email](img/unknown_sign_in_email_v14_0.png)

View File

@ -98,6 +98,9 @@ module API
optional :architecture, type: String, desc: %q(Runner's architecture)
optional :executor, type: String, desc: %q(Runner's executor)
optional :features, type: Hash, desc: %q(Runner's features)
optional :config, type: Hash, desc: %q(Runner's config) do
optional :gpus, type: String, desc: %q(GPUs enabled)
end
end
optional :session, type: Hash, desc: %q(Runner's session data) do
optional :url, type: String, desc: %q(Session's url)

View File

@ -25,6 +25,7 @@ module API
return get_runner_ip unless params['info'].present?
attributes_for_keys(%w(name version revision platform architecture), params['info'])
.merge(get_runner_config_from_request)
.merge(get_runner_ip)
end
@ -91,6 +92,12 @@ module API
def track_ci_minutes_usage!(_build, _runner)
# noop: overridden in EE
end
private
def get_runner_config_from_request
{ config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) }
end
end
end
end

View File

@ -18,10 +18,6 @@ module Gitlab
Feature.enabled?(:ci_pipeline_status_omit_commit_sha_in_cache_key, project, default_enabled: true)
end
def self.merge_base_pipeline_for_metrics_comparison?(project)
Feature.enabled?(:merge_base_pipeline_for_metrics_comparison, project, default_enabled: :yaml)
end
# NOTE: The feature flag `disallow_to_create_merge_request_pipelines_in_target_project`
# is a safe switch to disable the feature for a particular project when something went wrong,
# therefore it's not supposed to be enabled by default.

View File

@ -162,9 +162,6 @@ module Gitlab
# Sidekiq doesn't fork
return false if Gitlab::Runtime.sidekiq?
# Unicorn always forks
return true if Gitlab::Runtime.unicorn?
# Puma sometimes forks
return true if in_clustered_puma?

View File

@ -15,8 +15,7 @@ module Gitlab
:rails_runner,
:rake,
:sidekiq,
:test_suite,
:unicorn
:test_suite
].freeze
class << self
@ -36,11 +35,6 @@ module Gitlab
!!defined?(::Puma)
end
# For unicorn, we need to check for actual server instances to avoid false positives.
def unicorn?
!!(defined?(::Unicorn) && defined?(::Unicorn::HttpServer))
end
def sidekiq?
!!(defined?(::Sidekiq) && Sidekiq.server?)
end
@ -66,7 +60,7 @@ module Gitlab
end
def web_server?
puma? || unicorn?
puma?
end
def action_cable?

View File

@ -24645,9 +24645,6 @@ msgstr ""
msgid "Please provide a valid URL"
msgstr ""
msgid "Please provide a valid URL ending with .git"
msgstr ""
msgid "Please provide a valid URL."
msgstr ""

1
qa/.gitignore vendored
View File

@ -3,3 +3,4 @@ tmp/
.tool-versions
.ruby-gemset
urls.yml
reports/

View File

@ -4,6 +4,7 @@ source 'https://rubygems.org'
gem 'gitlab-qa'
gem 'activesupport', '~> 6.0.3.3' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.13.10'
gem 'capybara', '~> 3.29.0'
gem 'capybara-screenshot', '~> 1.0.23'
gem 'rake', '~> 12.3.3'

View File

@ -19,6 +19,14 @@ GEM
rack-test (>= 1.1.0, < 2.0)
rest-client (>= 2.0.2, < 3.0)
rspec (~> 3.8)
allure-rspec (2.13.10)
allure-ruby-commons (= 2.13.10)
rspec-core (>= 3.8, < 4)
allure-ruby-commons (2.13.10)
mime-types (>= 3.3, < 4)
oj (>= 3.10, < 4)
require_all (>= 2, < 4)
uuid (>= 2.3, < 3)
ast (2.4.1)
binding_ninja (0.2.3)
byebug (9.1.0)
@ -74,6 +82,8 @@ GEM
rake
launchy (2.4.3)
addressable (~> 2.3)
macaddr (1.7.2)
systemu (~> 2.6.5)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.9.0)
@ -96,6 +106,7 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.11.5)
parallel (1.19.2)
parallel_tests (2.29.0)
parallel
@ -119,6 +130,7 @@ GEM
rack (>= 1.0, < 3)
rake (12.3.3)
regexp_parser (1.6.0)
require_all (3.0.0)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
@ -154,6 +166,7 @@ GEM
selenium-webdriver (3.142.6)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
systemu (2.6.5)
thread_safe (0.3.6)
timecop (0.9.1)
tzinfo (1.2.9)
@ -169,6 +182,8 @@ GEM
equalizer (~> 0.0.9)
parser (>= 2.6.5)
procto (~> 0.0.2)
uuid (2.3.9)
macaddr (~> 1.0)
watir (6.18.0)
regexp_parser (>= 1.2, < 3)
selenium-webdriver (>= 3.8)
@ -182,6 +197,7 @@ PLATFORMS
DEPENDENCIES
activesupport (~> 6.0.3.3)
airborne (~> 0.3.4)
allure-rspec (~> 2.13.10)
capybara (~> 3.29.0)
capybara-screenshot (~> 1.0.23)
chemlab (~> 0.5)

View File

@ -45,6 +45,7 @@ module QA
autoload :IPAddress, 'qa/runtime/ip_address'
autoload :Search, 'qa/runtime/search'
autoload :ApplicationSettings, 'qa/runtime/application_settings'
autoload :AllureReport, 'qa/runtime/allure_report'
module API
autoload :Client, 'qa/runtime/api/client'

View File

@ -0,0 +1,77 @@
# frozen_string_literal: true
module QA
module Runtime
class AllureReport
class << self
# Configure allure reports
#
# @return [void]
def configure!
return unless Env.generate_allure_report?
require 'allure-rspec'
configure_allure
configure_attachments
configure_rspec
end
private
# Configure allure reporter
#
# @return [void]
def configure_allure
AllureRspec.configure do |config|
config.results_directory = 'tmp/allure-results'
config.clean_results_directory = true
end
end
# Set up failure screenshot attachments
#
# @return [void]
def configure_attachments
Capybara::Screenshot.after_save_screenshot do |path|
Allure.add_attachment(
name: 'screenshot',
source: File.open(path),
type: Allure::ContentType::PNG,
test_case: true
)
end
Capybara::Screenshot.after_save_html do |path|
Allure.add_attachment(
name: 'html',
source: File.open(path),
type: 'text/html',
test_case: true
)
end
end
# Configure rspec
#
# @return [void]
def configure_rspec
RSpec.configure do |config|
config.formatter = AllureRspecFormatter
config.before do |example|
next if example.attempts && example.attempts > 0
testcase = example.metadata[:testcase]
example.tms('Testcase', testcase) if testcase
issue = example.metadata.dig(:quarantine, :issue)
example.issue('Issue', issue) if issue
example.add_link(name: "Job(#{ENV['CI_JOB_NAME']})", url: ENV['CI_JOB_URL']) if ENV['CI']
end
end
end
end
end
end
end

View File

@ -2,7 +2,6 @@
require 'gitlab/qa'
require 'uri'
require 'active_support/core_ext/object/blank'
module QA
module Runtime
@ -53,6 +52,10 @@ module QA
enabled?(ENV['QA_DEBUG'], default: false)
end
def generate_allure_report?
enabled?(ENV['QA_GENERATE_ALLURE_REPORT'], default: false)
end
def default_branch
ENV['QA_DEFAULT_BRANCH'] || 'master'
end

View File

@ -19,15 +19,15 @@ module QA
define_singleton_method(attribute) do
attributes[attribute.to_sym].tap do |value|
if value.to_s.empty?
raise ArgumentError, "Empty `#{attribute}` attribute!"
end
raise ArgumentError, "Empty `#{attribute}` attribute!" if value.to_s.empty?
end
end
end
def from_env(var)
JSON.parse(Runtime::Env.runtime_scenario_attributes).each { |k, v| define(k, v) }
return if var.blank?
JSON.parse(var).each { |k, v| define(k, v) }
end
def method_missing(name, *)

View File

@ -4,6 +4,7 @@ require_relative '../qa'
require 'rspec/retry'
require 'rspec-parameterized'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/object/blank'
if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
require 'knapsack'
@ -11,8 +12,8 @@ if ENV['CI'] && QA::Runtime::Env.knapsack? && !ENV['NO_KNAPSACK']
end
QA::Runtime::Browser.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) if QA::Runtime::Env.runtime_scenario_attributes
QA::Runtime::AllureReport.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
Dir[::File.join(__dir__, "support/helpers/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "support/matchers/*.rb")].sort.each { |f| require f }

View File

@ -356,16 +356,6 @@ RSpec.describe 'New project', :js do
expect(git_import_instructions).to have_content 'Git repository URL'
end
it 'reports error if repo URL does not end with .git' do
fill_in 'project_import_url', with: 'http://foo/bar'
fill_in 'project_name', with: 'import-project-without-git-suffix'
fill_in 'project_path', with: 'import-project-without-git-suffix'
click_button 'Create project'
expect(page).to have_text('Please provide a valid URL ending with .git')
end
it 'keeps "Import project" tab open after form validation error' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)

View File

@ -49,6 +49,11 @@ RSpec.describe IssuesFinder do
let(:expected_issuables) { [issue3, issue4] }
end
it_behaves_like 'assignee OR filter' do
let(:params) { { or: { assignee_id: [user.id, user2.id] } } }
let(:expected_issuables) { [issue1, issue2, issue3, issue5] }
end
context 'when assignee_id does not exist' do
it_behaves_like 'assignee NOT ID filter' do
let(:params) { { not: { assignee_id: -100 } } }
@ -79,6 +84,11 @@ RSpec.describe IssuesFinder do
let(:expected_issuables) { [issue3, issue4] }
end
it_behaves_like 'assignee OR filter' do
let(:params) { { or: { assignee_username: [user2.username, user3.username] } } }
let(:expected_issuables) { [issue2, issue3] }
end
context 'when assignee_username does not exist' do
it_behaves_like 'assignee NOT username filter' do
before do

View File

@ -312,7 +312,7 @@ RSpec.describe IssuesHelper do
jira_integration_path: help_page_url('integration/jira/', anchor: 'view-jira-issues'),
markdown_help_path: help_page_path('user/markdown'),
max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes),
new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.id, milestone_id: finder.milestones.first.id }),
new_issue_path: new_project_issue_path(project, issue: { milestone_id: finder.milestones.first.id }),
project_import_jira_path: project_import_jira_path(project),
project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json),
project_milestones_path: project_milestones_path(project, format: :json),

View File

@ -0,0 +1,71 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Helpers::Runner do
let(:ip_address) { '1.2.3.4' }
let(:runner_class) do
Class.new do
include API::Helpers
include API::Helpers::Runner
attr_accessor :params
def initialize(params)
@params = params
end
def ip_address
'1.2.3.4'
end
end
end
let(:runner_helper) { runner_class.new(runner_params) }
describe '#get_runner_details_from_request' do
context 'when no runner info is present' do
let(:runner_params) { {} }
it 'returns the runner IP' do
expect(runner_helper.get_runner_details_from_request).to eq({ ip_address: ip_address })
end
end
context 'when runner info is present' do
let(:name) { 'runner' }
let(:version) { '1.2.3' }
let(:revision) { '10.0' }
let(:platform) { 'test' }
let(:architecture) { 'arm' }
let(:config) { { 'gpus' => 'all' } }
let(:runner_params) do
{
'info' =>
{
'name' => name,
'version' => version,
'revision' => revision,
'platform' => platform,
'architecture' => architecture,
'config' => config,
'ignored' => 1
}
}
end
subject(:details) { runner_helper.get_runner_details_from_request }
it 'extracts the runner details', :aggregate_failures do
expect(details.keys).to match_array(%w(name version revision platform architecture config ip_address))
expect(details['name']).to eq(name)
expect(details['version']).to eq(version)
expect(details['revision']).to eq(revision)
expect(details['platform']).to eq(platform)
expect(details['architecture']).to eq(architecture)
expect(details['config']).to eq(config)
expect(details['ip_address']).to eq(ip_address)
end
end
end
end

View File

@ -4,7 +4,15 @@ require 'spec_helper'
RSpec.describe Gitlab::ErrorTracking::Processor::ContextPayloadProcessor do
describe '.call' do
let(:event) { Raven::Event.new(payload) }
let(:required_options) do
{
configuration: Raven.configuration,
context: Raven.context,
breadcrumbs: Raven.breadcrumbs
}
end
let(:event) { Raven::Event.new(required_options.merge(payload)) }
let(:result_hash) { described_class.call(event).to_hash }
before do

View File

@ -4,7 +4,15 @@ require 'spec_helper'
RSpec.describe Gitlab::ErrorTracking::Processor::GrpcErrorProcessor do
describe '.call' do
let(:event) { Raven::Event.from_exception(exception, data) }
let(:required_options) do
{
configuration: Raven.configuration,
context: Raven.context,
breadcrumbs: Raven.breadcrumbs
}
end
let(:event) { Raven::Event.from_exception(exception, required_options.merge(data)) }
let(:result_hash) { described_class.call(event).to_hash }
context 'when there is no GRPC exception' do

View File

@ -95,7 +95,15 @@ RSpec.describe Gitlab::ErrorTracking::Processor::SidekiqProcessor do
end
describe '.call' do
let(:event) { Raven::Event.new(wrapped_value) }
let(:required_options) do
{
configuration: Raven.configuration,
context: Raven.context,
breadcrumbs: Raven.breadcrumbs
}
end
let(:event) { Raven::Event.new(required_options.merge(wrapped_value)) }
let(:result_hash) { described_class.call(event).to_hash }
context 'when there is Sidekiq data' do

View File

@ -337,7 +337,7 @@ RSpec.describe Emails::Profile do
end
it 'mentioned the time' do
is_expected.to have_body_text current_time.strftime('%Y-%m-%d %l:%M:%S %p %Z')
is_expected.to have_body_text current_time.strftime('%Y-%m-%d %k:%M:%S %Z')
end
it 'includes a link to the change password documentation' do

View File

@ -75,6 +75,22 @@ RSpec.describe Ci::Runner do
expect { create(:group, runners: [project_runner]) }
.to raise_error(ActiveRecord::RecordInvalid)
end
context 'when runner has config' do
it 'is valid' do
runner = build(:ci_runner, config: { gpus: "all" })
expect(runner).to be_valid
end
end
context 'when runner has an invalid config' do
it 'is invalid' do
runner = build(:ci_runner, config: { test: 1 })
expect(runner).not_to be_valid
end
end
end
context 'cost factors validations' do
@ -653,7 +669,7 @@ RSpec.describe Ci::Runner do
describe '#heartbeat' do
let(:runner) { create(:ci_runner, :project) }
subject { runner.heartbeat(architecture: '18-bit') }
subject { runner.heartbeat(architecture: '18-bit', config: { gpus: "all" }) }
context 'when database was updated recently' do
before do
@ -701,6 +717,7 @@ RSpec.describe Ci::Runner do
def does_db_update
expect { subject }.to change { runner.reload.read_attribute(:contacted_at) }
.and change { runner.reload.read_attribute(:architecture) }
.and change { runner.reload.read_attribute(:config) }
end
end

View File

@ -37,6 +37,10 @@ RSpec.describe GroupMember do
end
end
describe 'delegations' do
it { is_expected.to delegate_method(:update_two_factor_requirement).to(:user).allow_nil }
end
describe '.access_level_roles' do
it 'returns Gitlab::Access.options_with_owner' do
expect(described_class.access_level_roles).to eq(Gitlab::Access.options_with_owner)
@ -93,6 +97,18 @@ RSpec.describe GroupMember do
end
end
describe '#destroy' do
context 'for an orphaned member' do
let!(:orphaned_group_member) do
create(:group_member).tap { |member| member.update_column(:user_id, nil) }
end
it 'does not raise an error' do
expect { orphaned_group_member.destroy! }.not_to raise_error
end
end
end
describe '#after_accept_invite' do
it 'calls #update_two_factor_requirement' do
email = 'foo@email.com'

View File

@ -58,6 +58,16 @@ RSpec.describe ProjectMember do
maintainer.destroy!
expect(Event.recent.first).to be_left_action
end
context 'for an orphaned member' do
let!(:orphaned_project_member) do
owner.tap { |member| member.update_column(:user_id, nil) }
end
it 'does not raise an error' do
expect { orphaned_project_member.destroy! }.not_to raise_error
end
end
end
describe '.import_team' do

View File

@ -3912,14 +3912,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
let(:service_class) { 'Ci::CompareMetricsReportsService' }
it { is_expected.to be_truthy }
context 'with the metrics report flag disabled' do
before do
stub_feature_flags(merge_base_pipeline_for_metrics_comparison: false)
end
it { is_expected.to be_falsey }
end
end
context 'when service class is Ci::CompareCodequalityReportsService' do

View File

@ -1,105 +0,0 @@
# frozen_string_literal: true
require 'fileutils'
require 'excon'
require 'spec_helper'
RSpec.describe 'Unicorn' do
before(:all) do
project_root = File.expand_path('../..', __dir__)
config_lines = File.read('config/unicorn.rb.example')
.gsub('/home/git/gitlab', project_root)
.gsub('/home/git', project_root)
.split("\n")
# Remove these because they make setup harder.
config_lines = config_lines.reject do |line|
%w[
worker_processes
listen
pid
stderr_path
stdout_path
].any? { |prefix| line.start_with?(prefix) }
end
config_lines << "working_directory '#{Rails.root}'"
# We want to have exactly 1 worker process because that makes it
# predictable which process will handle our requests.
config_lines << 'worker_processes 1'
@socket_path = File.join(project_root, 'tmp/tests/unicorn.socket')
config_lines << "listen '#{@socket_path}'"
ready_file = File.join(project_root, 'tmp/tests/unicorn-worker-ready')
FileUtils.rm_f(ready_file)
after_fork_index = config_lines.index { |l| l.start_with?('after_fork') }
config_lines.insert(after_fork_index + 1, "File.write('#{ready_file}', Process.pid)")
config_path = File.join(project_root, 'tmp/tests/unicorn.rb')
File.write(config_path, config_lines.join("\n") + "\n")
cmd = %W[unicorn -E test -c #{config_path} spec/rack_servers/configs/config.ru]
@unicorn_master_pid = spawn(*cmd)
wait_unicorn_boot!(@unicorn_master_pid, ready_file)
WebMock.allow_net_connect!
end
%w[SIGQUIT SIGTERM SIGKILL].each do |signal|
it "has a worker that self-terminates on signal #{signal}" do
response = Excon.get('unix://', socket: @socket_path)
expect(response.status).to eq(200)
worker_pid = response.body.to_i
expect(worker_pid).to be > 0
begin
Excon.post("unix://?#{signal}", socket: @socket_path)
rescue Excon::Error::Socket
# The connection may be closed abruptly
end
expect(pid_gone?(worker_pid)).to eq(true)
end
end
after(:all) do
webmock_enable!
Process.kill('TERM', @unicorn_master_pid)
end
def wait_unicorn_boot!(master_pid, ready_file)
# We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
timeout = 5 * 60
timeout.times do
return if File.exist?(ready_file)
pid = Process.waitpid(master_pid, Process::WNOHANG)
raise "unicorn failed to boot: #{$?}" unless pid.nil?
sleep 1
end
raise "unicorn boot timed out after #{timeout} seconds"
end
def pid_gone?(pid)
# Worker termination should take less than a second. That makes 10
# seconds a generous timeout.
10.times do
begin
Process.kill(0, pid)
rescue Errno::ESRCH
return true
end
sleep 1
end
false
end
end

View File

@ -439,6 +439,13 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
it "sets the runner's config" do
request_job info: { 'config' => { 'gpus' => 'all', 'ignored' => 'hello' } }
expect(response).to have_gitlab_http_status(:created)
expect(runner.reload.config).to eq( { 'gpus' => 'all' } )
end
it "sets the runner's ip_address" do
post api('/jobs/request'),
params: { token: runner.token },

View File

@ -195,6 +195,14 @@ RSpec.describe DesignManagement::CopyDesignCollection::CopyService, :clean_gitla
expect { subject }.to change { target_repository.branch_names }.from([]).to(['master'])
end
it 'does not create default branch when one exists' do
target_repository.create_if_not_exists
target_repository.create_file(user, '.meta', '.gitlab', branch_name: 'new-branch', message: 'message')
expect { subject }.not_to change { target_repository.branch_names }
expect(target_repository.branch_names).to eq(['new-branch'])
end
it 'leaves the design collection in the correct copy state' do
subject

View File

@ -24,6 +24,12 @@ RSpec.shared_examples 'assignee NOT username filter' do
end
end
RSpec.shared_examples 'assignee OR filter' do
it 'returns issuables assigned to the given users' do
expect(issuables).to contain_exactly(*expected_issuables)
end
end
RSpec.shared_examples 'no assignee filter' do
let(:params) { { assignee_id: 'None' } }