diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index ac9dfe217bd..b9a2eb3ebc6 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -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
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 4fb55ecaf6b..ae15995215a 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -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'
diff --git a/Gemfile b/Gemfile
index 25fb430c290..0d0ac74bf1e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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'
diff --git a/Gemfile.lock b/Gemfile.lock
index c08dea0c831..196eeb8ea60 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index cf7970f41b1..4bbb292ad94 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -79,13 +79,20 @@ export default {
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index d1885b5ae08..086dadcf5b7 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -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
diff --git a/app/finders/issuable_finder/params.rb b/app/finders/issuable_finder/params.rb
index a62210ceac5..51e12dde51d 100644
--- a/app/finders/issuable_finder/params.rb
+++ b/app/finders/issuable_finder/params.rb
@@ -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]
diff --git a/app/finders/issuables/assignee_filter.rb b/app/finders/issuables/assignee_filter.rb
new file mode 100644
index 00000000000..2e58a6b34c9
--- /dev/null
+++ b/app/finders/issuables/assignee_filter.rb
@@ -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
diff --git a/app/finders/issuables/author_filter.rb b/app/finders/issuables/author_filter.rb
index 522751a384e..f36daae553d 100644
--- a/app/finders/issuables/author_filter.rb
+++ b/app/finders/issuables/author_filter.rb
@@ -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
diff --git a/app/finders/issuables/base_filter.rb b/app/finders/issuables/base_filter.rb
index 6d1a3f96062..7c607e2d048 100644
--- a/app/finders/issuables/base_filter.rb
+++ b/app/finders/issuables/base_filter.rb
@@ -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
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index eb9099fe256..40d6730d232 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -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?
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 8331ba15cd5..91920277c50 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -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),
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 6d398471dcd..144595ff7b0 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -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)
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index f5c70f10dc5..b4289a0d131 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -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
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index b22a4fa9ef6..61e89125b5f 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -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
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 32db878ffe0..1844be5d2e4 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -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
diff --git a/app/services/design_management/copy_design_collection/copy_service.rb b/app/services/design_management/copy_design_collection/copy_service.rb
index 496103f9e58..b40f6a81174 100644
--- a/app/services/design_management/copy_design_collection/copy_service.rb
+++ b/app/services/design_management/copy_design_collection/copy_service.rb
@@ -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
diff --git a/app/services/design_management/design_service.rb b/app/services/design_management/design_service.rb
index 5aa2a2f73bc..f337a9dc8e0 100644
--- a/app/services/design_management/design_service.rb
+++ b/app/services/design_management/design_service.rb
@@ -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
diff --git a/app/validators/json_schemas/ci_runner_config.json b/app/validators/json_schemas/ci_runner_config.json
new file mode 100644
index 00000000000..af1bcfcb183
--- /dev/null
+++ b/app/validators/json_schemas/ci_runner_config.json
@@ -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
+}
diff --git a/app/views/notify/unknown_sign_in_email.html.haml b/app/views/notify/unknown_sign_in_email.html.haml
index 8d0993e9ff8..464bcef1474 100644
--- a/app/views/notify/unknown_sign_in_email.html.haml
+++ b/app/views/notify/unknown_sign_in_email.html.haml
@@ -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 }
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index c0fe788b56a..e6ded3ad912 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -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
diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml
index 06522d9d434..1289f7aa0c4 100644
--- a/app/views/projects/issues/_nav_btns.html.haml
+++ b/app/views/projects/issues/_nav_btns.html.haml
@@ -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"
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index cf9ee1a5231..65e02341936 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -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
diff --git a/config/feature_flags/development/merge_base_pipeline_for_metrics_comparison.yml b/config/feature_flags/development/merge_base_pipeline_for_metrics_comparison.yml
deleted file mode 100644
index 1fdb8d5bc6d..00000000000
--- a/config/feature_flags/development/merge_base_pipeline_for_metrics_comparison.yml
+++ /dev/null
@@ -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
diff --git a/config/initializers/rbtrace.rb b/config/initializers/rbtrace.rb
index 6a1b71bf4bd..2359fc9f6b5 100644
--- a/config/initializers/rbtrace.rb
+++ b/config/initializers/rbtrace.rb
@@ -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
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
deleted file mode 100644
index c930e2ff761..00000000000
--- a/config/unicorn.rb.example
+++ /dev/null
@@ -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
diff --git a/config/unicorn.rb.example.development b/config/unicorn.rb.example.development
deleted file mode 100644
index 2c6e809f753..00000000000
--- a/config/unicorn.rb.example.development
+++ /dev/null
@@ -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
diff --git a/db/migrate/20210331000934_add_config_to_ci_runners.rb b/db/migrate/20210331000934_add_config_to_ci_runners.rb
new file mode 100644
index 00000000000..e9a5fadc613
--- /dev/null
+++ b/db/migrate/20210331000934_add_config_to_ci_runners.rb
@@ -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
diff --git a/db/schema_migrations/20210331000934 b/db/schema_migrations/20210331000934
new file mode 100644
index 00000000000..1d55b126d60
--- /dev/null
+++ b/db/schema_migrations/20210331000934
@@ -0,0 +1 @@
+0bd47f9055aab927a4e8efb4f995f44532880926af9892af60f7d2b8dcdef4a6
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 5c5aeaf93a9..d550c0f7eaa 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index aff392f61ff..136580f5e0f 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -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:
diff --git a/doc/user/profile/img/unknown_sign_in_email_v13_1.png b/doc/user/profile/img/unknown_sign_in_email_v13_1.png
deleted file mode 100644
index 586be483be9..00000000000
Binary files a/doc/user/profile/img/unknown_sign_in_email_v13_1.png and /dev/null differ
diff --git a/doc/user/profile/img/unknown_sign_in_email_v14_0.png b/doc/user/profile/img/unknown_sign_in_email_v14_0.png
new file mode 100644
index 00000000000..dec1251addb
Binary files /dev/null and b/doc/user/profile/img/unknown_sign_in_email_v14_0.png differ
diff --git a/doc/user/profile/unknown_sign_in_notification.md b/doc/user/profile/unknown_sign_in_notification.md
index 1eec351e4da..7aa1ae89c9f 100644
--- a/doc/user/profile/unknown_sign_in_notification.md
+++ b/doc/user/profile/unknown_sign_in_notification.md
@@ -30,4 +30,4 @@ for a notification email to be sent.
## Example email
-
+
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 33980b38e2b..035711da1a0 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -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)
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 6f25cf507bc..e141abeef91 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -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
diff --git a/lib/gitlab/ci/features.rb b/lib/gitlab/ci/features.rb
index 37700131346..d1c1a4ecbde 100644
--- a/lib/gitlab/ci/features.rb
+++ b/lib/gitlab/ci/features.rb
@@ -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.
diff --git a/lib/gitlab/cluster/lifecycle_events.rb b/lib/gitlab/cluster/lifecycle_events.rb
index 6b7b9b09269..6159fb0a811 100644
--- a/lib/gitlab/cluster/lifecycle_events.rb
+++ b/lib/gitlab/cluster/lifecycle_events.rb
@@ -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?
diff --git a/lib/gitlab/runtime.rb b/lib/gitlab/runtime.rb
index b0bcea0ca69..f60cac0aff0 100644
--- a/lib/gitlab/runtime.rb
+++ b/lib/gitlab/runtime.rb
@@ -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?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0fd492b87f4..60b3c61ab0e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/qa/.gitignore b/qa/.gitignore
index 2095d5c722c..b54b8666e28 100644
--- a/qa/.gitignore
+++ b/qa/.gitignore
@@ -3,3 +3,4 @@ tmp/
.tool-versions
.ruby-gemset
urls.yml
+reports/
diff --git a/qa/Gemfile b/qa/Gemfile
index 8b3a9802000..3b9cf02c2f4 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -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'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 9bac86bad08..84ac3e0d69d 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -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)
diff --git a/qa/qa.rb b/qa/qa.rb
index bf86fc5faec..ec8572fd7df 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -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'
diff --git a/qa/qa/runtime/allure_report.rb b/qa/qa/runtime/allure_report.rb
new file mode 100644
index 00000000000..cf8d33e0a6d
--- /dev/null
+++ b/qa/qa/runtime/allure_report.rb
@@ -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
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index e649084a394..e7c9478fa54 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -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
diff --git a/qa/qa/runtime/scenario.rb b/qa/qa/runtime/scenario.rb
index 3662ebe671b..d44cc846128 100644
--- a/qa/qa/runtime/scenario.rb
+++ b/qa/qa/runtime/scenario.rb
@@ -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, *)
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 0c9643c830b..f4bfd57504e 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -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 }
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index a1523f9eb08..0bb22afdd15 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -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)
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 27466ab563f..1c8c2af8e03 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -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
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index ceca83d107b..59b42dfca20 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -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),
diff --git a/spec/lib/api/helpers/runner_helpers_spec.rb b/spec/lib/api/helpers/runner_helpers_spec.rb
new file mode 100644
index 00000000000..65b35845aab
--- /dev/null
+++ b/spec/lib/api/helpers/runner_helpers_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
index 584eadb24a7..210829056c8 100644
--- a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
+++ b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb
index 727b603feda..6076e525f06 100644
--- a/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb
+++ b/spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb
index c8a362fcf05..af5f11c9362 100644
--- a/spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb
+++ b/spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb
@@ -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
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index 8ac1f15d67e..f6895cab30e 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -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
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 9f299b78a8a..491088a44a1 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -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
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 3a2db5d8516..8c942228059 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -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'
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index fa77e319c2c..b84b408cb4b 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -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
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 3fb3225ebbc..7cbfefd421d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -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
diff --git a/spec/rack_servers/unicorn_spec.rb b/spec/rack_servers/unicorn_spec.rb
deleted file mode 100644
index 52d44b6e7e0..00000000000
--- a/spec/rack_servers/unicorn_spec.rb
+++ /dev/null
@@ -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
diff --git a/spec/requests/api/ci/runner/jobs_request_post_spec.rb b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
index 63da3340a45..cd2fa2ded23 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -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 },
diff --git a/spec/services/design_management/copy_design_collection/copy_service_spec.rb b/spec/services/design_management/copy_design_collection/copy_service_spec.rb
index 03242487b53..186d2481c19 100644
--- a/spec/services/design_management/copy_design_collection/copy_service_spec.rb
+++ b/spec/services/design_management/copy_design_collection/copy_service_spec.rb
@@ -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
diff --git a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
index 96b05db4cd9..5cbbed1468f 100644
--- a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
+++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb
@@ -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' } }