Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d380b256b9
commit
faaef22ae3
|
|
@ -811,6 +811,10 @@ entry.
|
|||
- [Propagate RemoteIP to Gitaly via Workhorse](gitlab-org/gitlab@71da945c85931bac0263c193902dc1b54e2e62da) ([merge request](gitlab-org/gitlab!103635))
|
||||
- [Documentation to reflect 100MB upload limit](gitlab-org/gitlab@33063bb26ab7699802ecb2b325cc8619d6fe7b86) ([merge request](gitlab-org/gitlab!103978))
|
||||
|
||||
## 15.6.3 (2022-12-21)
|
||||
|
||||
No changes.
|
||||
|
||||
## 15.6.2 (2022-12-05)
|
||||
|
||||
### Added (1 change)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ query getJobArtifacts(
|
|||
project(fullPath: $projectPath) {
|
||||
id
|
||||
jobs(
|
||||
withArtifacts: true
|
||||
statuses: [SUCCESS, FAILED]
|
||||
first: $firstPageSize
|
||||
last: $lastPageSize
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Jed from 'jed';
|
||||
import ensureSingleLine from './ensure_single_line';
|
||||
import ensureSingleLine from './ensure_single_line.cjs';
|
||||
import sprintf from './sprintf';
|
||||
|
||||
const GITLAB_FALLBACK_LANGUAGE = 'en';
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
|
||||
push_frontend_feature_flag(:use_iid_in_work_items_path, project)
|
||||
push_frontend_feature_flag(:use_iid_in_work_items_path, project&.group)
|
||||
push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ class Projects::WorkItemsController < Projects::ApplicationController
|
|||
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?)
|
||||
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:use_iid_in_work_items_path, project)
|
||||
push_frontend_feature_flag(:use_iid_in_work_items_path, project&.group)
|
||||
end
|
||||
|
||||
feature_category :team_planning
|
||||
|
|
|
|||
|
|
@ -4,10 +4,21 @@ module Types
|
|||
class Namespace::SharedRunnersSettingEnum < BaseEnum
|
||||
graphql_name 'SharedRunnersSetting'
|
||||
|
||||
::Namespace::SHARED_RUNNERS_SETTINGS.each do |type|
|
||||
DEPRECATED_SETTINGS = [::Namespace::SR_DISABLED_WITH_OVERRIDE].freeze
|
||||
|
||||
::Namespace::SHARED_RUNNERS_SETTINGS.excluding(DEPRECATED_SETTINGS).each do |type|
|
||||
value type.upcase,
|
||||
description: "Sharing of runners is #{type.tr('_', ' ')}.",
|
||||
value: type
|
||||
end
|
||||
|
||||
value ::Namespace::SR_DISABLED_WITH_OVERRIDE.upcase,
|
||||
description: "Sharing of runners is disabled and overridable.",
|
||||
value: ::Namespace::SR_DISABLED_WITH_OVERRIDE,
|
||||
deprecated: {
|
||||
reason: :renamed,
|
||||
replacement: ::Namespace::SR_DISABLED_AND_OVERRIDABLE,
|
||||
milestone: "17.0"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ module Ci
|
|||
parent_shared_runners_setting: group.parent&.shared_runners_setting,
|
||||
runner_enabled_value: Namespace::SR_ENABLED,
|
||||
runner_disabled_value: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
|
||||
runner_allow_override_value: Namespace::SR_DISABLED_WITH_OVERRIDE
|
||||
runner_allow_override_value: Namespace::SR_DISABLED_AND_OVERRIDABLE
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -815,7 +815,7 @@ class Group < Namespace
|
|||
|
||||
case state
|
||||
when SR_DISABLED_AND_UNOVERRIDABLE then disable_shared_runners! # also disallows override
|
||||
when SR_DISABLED_WITH_OVERRIDE then disable_shared_runners_and_allow_override!
|
||||
when SR_DISABLED_WITH_OVERRIDE, SR_DISABLED_AND_OVERRIDABLE then disable_shared_runners_and_allow_override!
|
||||
when SR_ENABLED then enable_shared_runners! # set both to true
|
||||
end
|
||||
end
|
||||
|
|
@ -1055,7 +1055,7 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def disable_shared_runners_and_allow_override!
|
||||
# enabled -> disabled_with_override
|
||||
# enabled -> disabled_and_overridable
|
||||
if shared_runners_enabled?
|
||||
update!(
|
||||
shared_runners_enabled: false,
|
||||
|
|
@ -1068,7 +1068,7 @@ class Group < Namespace
|
|||
|
||||
all_projects.update_all(shared_runners_enabled: false)
|
||||
|
||||
# disabled_and_unoverridable -> disabled_with_override
|
||||
# disabled_and_unoverridable -> disabled_and_overridable
|
||||
else
|
||||
update!(allow_descendants_override_disabled_shared_runners: true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ class Namespace < ApplicationRecord
|
|||
NUMBER_OF_ANCESTORS_ALLOWED = 20
|
||||
|
||||
SR_DISABLED_AND_UNOVERRIDABLE = 'disabled_and_unoverridable'
|
||||
# DISABLED_WITH_OVERRIDE is deprecated in favour of DISABLED_AND_OVERRIDABLE.
|
||||
SR_DISABLED_WITH_OVERRIDE = 'disabled_with_override'
|
||||
SR_DISABLED_AND_OVERRIDABLE = 'disabled_and_overridable'
|
||||
SR_ENABLED = 'enabled'
|
||||
SHARED_RUNNERS_SETTINGS = [SR_DISABLED_AND_UNOVERRIDABLE, SR_DISABLED_WITH_OVERRIDE, SR_ENABLED].freeze
|
||||
SHARED_RUNNERS_SETTINGS = [SR_DISABLED_AND_UNOVERRIDABLE, SR_DISABLED_WITH_OVERRIDE, SR_DISABLED_AND_OVERRIDABLE, SR_ENABLED].freeze
|
||||
URL_MAX_LENGTH = 255
|
||||
|
||||
PATH_TRAILING_VIOLATIONS = %w[.git .atom .].freeze
|
||||
|
|
@ -556,7 +558,7 @@ class Namespace < ApplicationRecord
|
|||
if shared_runners_enabled
|
||||
SR_ENABLED
|
||||
elsif allow_descendants_override_disabled_shared_runners
|
||||
SR_DISABLED_WITH_OVERRIDE
|
||||
SR_DISABLED_AND_OVERRIDABLE
|
||||
else
|
||||
SR_DISABLED_AND_UNOVERRIDABLE
|
||||
end
|
||||
|
|
@ -566,10 +568,10 @@ class Namespace < ApplicationRecord
|
|||
case other_setting
|
||||
when SR_ENABLED
|
||||
false
|
||||
when SR_DISABLED_WITH_OVERRIDE
|
||||
when SR_DISABLED_WITH_OVERRIDE, SR_DISABLED_AND_OVERRIDABLE
|
||||
shared_runners_setting == SR_ENABLED
|
||||
when SR_DISABLED_AND_UNOVERRIDABLE
|
||||
shared_runners_setting == SR_ENABLED || shared_runners_setting == SR_DISABLED_WITH_OVERRIDE
|
||||
shared_runners_setting == SR_ENABLED || shared_runners_setting == SR_DISABLED_AND_OVERRIDABLE || shared_runners_setting == SR_DISABLED_WITH_OVERRIDE
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ module Projects
|
|||
.then do |(import_url, resolved_host)|
|
||||
next '' if resolved_host.nil? || !import_url.scheme.in?(%w[http https])
|
||||
|
||||
import_url.host.to_s
|
||||
import_url.hostname.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@
|
|||
.nav-controls
|
||||
= render_if_exists 'admin/users/admin_email_users'
|
||||
= render_if_exists 'admin/users/admin_export_user_permissions'
|
||||
= link_to s_('AdminUsers|New user'), new_admin_user_path, class: 'btn gl-button btn-confirm btn-search float-right'
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, href: new_admin_user_path) do
|
||||
= s_('AdminUsers|New user')
|
||||
|
||||
.filtered-search-block.row-content-block.border-top-0
|
||||
= form_tag admin_users_path, method: :get do
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
= link_to _('Delete'), project, data: { confirm: remove_project_message(project) }, method: :delete, class: "btn gl-button btn-danger"
|
||||
= render Pajamas::ButtonComponent.new(href: project, variant: :danger, method: :delete, button_options: { data: { confirm: remove_project_message(project) } }) do
|
||||
= _('Delete')
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- @no_breadcrumb_border = true
|
||||
- show_auto_devops_callout = show_auto_devops_callout?(@project)
|
||||
- is_project_overview = local_assigns.fetch(:is_project_overview, false)
|
||||
- ref = local_assigns.fetch(:ref) { current_ref }
|
||||
|
|
|
|||
|
|
@ -313,21 +313,21 @@ module.exports = {
|
|||
use: [],
|
||||
},
|
||||
WEBPACK_USE_ESBUILD_LOADER && {
|
||||
test: /\.js$/,
|
||||
test: /\.(js|cjs)$/,
|
||||
exclude: (modulePath) =>
|
||||
/node_modules|vendor[\\/]assets/.test(modulePath) && !/\.vue\.js/.test(modulePath),
|
||||
loader: 'esbuild-loader',
|
||||
options: esbuildConfiguration,
|
||||
},
|
||||
!WEBPACK_USE_ESBUILD_LOADER && {
|
||||
test: /\.js$/,
|
||||
test: /\.(js|cjs)$/,
|
||||
exclude: (modulePath) =>
|
||||
/node_modules|vendor[\\/]assets/.test(modulePath) && !/\.vue\.js/.test(modulePath),
|
||||
loader: 'babel-loader',
|
||||
options: defaultJsOptions,
|
||||
},
|
||||
WEBPACK_USE_ESBUILD_LOADER && {
|
||||
test: /\.js$/,
|
||||
test: /\.(js|cjs)$/,
|
||||
include: (modulePath) =>
|
||||
/node_modules\/(monaco-worker-manager|monaco-marker-data-provider)\/index\.js/.test(
|
||||
modulePath,
|
||||
|
|
@ -336,7 +336,7 @@ module.exports = {
|
|||
options: esbuildConfiguration,
|
||||
},
|
||||
!WEBPACK_USE_ESBUILD_LOADER && {
|
||||
test: /\.js$/,
|
||||
test: /\.(js|cjs)$/,
|
||||
include: (modulePath) =>
|
||||
/node_modules\/(monaco-worker-manager|monaco-marker-data-provider)\/index\.js/.test(
|
||||
modulePath,
|
||||
|
|
|
|||
|
|
@ -22777,8 +22777,9 @@ How to format SHA strings.
|
|||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="sharedrunnerssettingdisabled_and_overridable"></a>`DISABLED_AND_OVERRIDABLE` | Sharing of runners is disabled and overridable. |
|
||||
| <a id="sharedrunnerssettingdisabled_and_unoverridable"></a>`DISABLED_AND_UNOVERRIDABLE` | Sharing of runners is disabled and unoverridable. |
|
||||
| <a id="sharedrunnerssettingdisabled_with_override"></a>`DISABLED_WITH_OVERRIDE` | Sharing of runners is disabled with override. |
|
||||
| <a id="sharedrunnerssettingdisabled_with_override"></a>`DISABLED_WITH_OVERRIDE` **{warning-solid}** | **Deprecated** in 17.0. This was renamed. Use: `disabled_and_overridable`. |
|
||||
| <a id="sharedrunnerssettingenabled"></a>`ENABLED` | Sharing of runners is enabled. |
|
||||
|
||||
### `SnippetBlobActionEnum`
|
||||
|
|
|
|||
|
|
@ -1099,8 +1099,9 @@ The `shared_runners_setting` attribute determines whether shared runners are ena
|
|||
| Value | Description |
|
||||
|-------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `enabled` | Enables shared runners for all projects and subgroups in this group. |
|
||||
| `disabled_with_override` | Disables shared runners for all projects and subgroups in this group, but allows subgroups to override this setting. |
|
||||
| `disabled_and_overridable` | Disables shared runners for all projects and subgroups in this group, but allows subgroups to override this setting. |
|
||||
| `disabled_and_unoverridable` | Disables shared runners for all projects and subgroups in this group, and prevents subgroups from overriding this setting. |
|
||||
| `disabled_with_override` | (Deprecated. Use `disabled_and_overridable`) Disables shared runners for all projects and subgroups in this group, but allows subgroups to override this setting. |
|
||||
|
||||
### Upload a group avatar
|
||||
|
||||
|
|
|
|||
|
|
@ -150,6 +150,14 @@ At this moment, GitLab.com has "social-network"-like capabilities that may not f
|
|||
|
||||
We should evaluate if the SMB and mid market segment is interested in these features, or if not having them is acceptable in most cases.
|
||||
|
||||
### Self-managed
|
||||
|
||||
For reasons of consistency, it is expected that self-managed instances will
|
||||
adopt the pods architecture as well. To expand, self-managed instances can
|
||||
continue with just a single Pod while supporting the option of adding additional
|
||||
Pods. Organizations, and possible User decomposition will also be adopted for
|
||||
self-managed instances.
|
||||
|
||||
## High-level architecture problems to solve
|
||||
|
||||
A number of technical issues need to be resolved to implement Pods (in no particular order). This section will be expanded.
|
||||
|
|
@ -325,6 +333,7 @@ This is the list of known affected features with the proposed solutions.
|
|||
- [Pods: Organizations](pods-feature-organizations.md)
|
||||
- [Pods: Router Endpoints Classification](pods-feature-router-endpoints-classification.md)
|
||||
- [Pods: Schema changes (Postgres and Elasticsearch migrations)](pods-feature-schema-changes.md)
|
||||
- [Pods: Backups](pods-feature-backups.md)
|
||||
- [Pods: Global Search](pods-feature-global-search.md)
|
||||
- [Pods: CI Runners](pods-feature-ci-runners.md)
|
||||
- [Pods: Admin Area](pods-feature-admin-area.md)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
stage: enablement
|
||||
group: pods
|
||||
comments: false
|
||||
description: 'Pods: Backups'
|
||||
---
|
||||
|
||||
This document is a work-in-progress and represents a very early state of the
|
||||
Pods design. Significant aspects are not documented, though we expect to add
|
||||
them in the future. This is one possible architecture for Pods, and we intend to
|
||||
contrast this with alternatives before deciding which approach to implement.
|
||||
This documentation will be kept even if we decide not to implement this so that
|
||||
we can document the reasons for not choosing this approach.
|
||||
|
||||
# Pods: Backups
|
||||
|
||||
Each pods will take its own backups, and consequently have its own isolated
|
||||
backup / restore procedure.
|
||||
|
||||
## 1. Definition
|
||||
|
||||
GitLab Backup takes a backup of the PostgreSQL database used by the application,
|
||||
and also Git repository data.
|
||||
|
||||
## 2. Data flow
|
||||
|
||||
Each pod has a number of application databases to back up (e.g. `main`, and `ci`).
|
||||
|
||||
Additionally, there may be cluster-wide metadata tables (e.g. `users` table)
|
||||
which is directly accesible via PostgreSQL.
|
||||
|
||||
## 3. Proposal
|
||||
|
||||
### 3.1. Cluster-wide metadata
|
||||
|
||||
It is currently unknown how cluster-wide metadata tables will be accessible. We
|
||||
may choose to have cluster-wide metadata tables backed up separately, or have
|
||||
each pod back up its copy of cluster-wide metdata tables.
|
||||
|
||||
### 3.2 Consistency
|
||||
|
||||
#### 3.2.1 Take backups independently
|
||||
|
||||
As each pod will communicate with each other via API, and there will be no joins
|
||||
to the users table, it should be acceptable for each pod to take a backup
|
||||
independently of each other.
|
||||
|
||||
#### 3.2.2 Enforce snapshots
|
||||
|
||||
We can require that each pod take a snapshot for the PostgreSQL databases at
|
||||
around the same time to allow for a consistent-enough backup.
|
||||
|
||||
## 4. Evaluation
|
||||
|
||||
As the number of pods increases, it will likely not be feasible to take a
|
||||
snapshot at the same time for all pods. Hence taking backups independently is
|
||||
the better option.
|
||||
|
||||
## 4.1. Pros
|
||||
|
||||
## 4.2. Cons
|
||||
|
|
@ -18,7 +18,7 @@ The intent is not to retroactively change names in existing databases but rather
|
|||
|--------------------------|---------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||
| **Primary Key** | `pk_<table name>` | | `pk_projects` |
|
||||
| **Foreign Key** | `fk_<table name>_<column name>[_and_<column name>]*_<foreign table name>` | | `fk_projects_group_id_groups` |
|
||||
| **Index** | `index_<table name>_on_<column name>[_and_<column name>]*[_and_<column name in partial clause>]*` | | `index_repositories_on_group_id` |
|
||||
| **Index** | `index_<table name>_on_<column name>[_and_<column name>]*[_and_<column name in partial clause>]*` | Index names must be all lowercase. | `index_repositories_on_group_id` |
|
||||
| **Unique Constraint** | `unique_<table name>_<column name>[_and_<column name>]*` | | `unique_projects_group_id_and_name` |
|
||||
| **Check Constraint** | `check_<table name>_<column name>[_and_<column name>]*[_<suffix>]?` | The optional suffix should denote the type of validation, such as `length` and `enum`. It can also be used to disambiguate multiple `CHECK` constraints on the same column. | `check_projects_name_length`<br />`check_projects_type_enum`<br />`check_projects_admin1_id_and_admin2_id_differ` |
|
||||
| **Exclusion Constraint** | `excl_<table name>_<column name>[_and_<column name>]*_[_<suffix>]?` | The optional suffix should denote the type of exclusion being performed. | `excl_reservations_start_at_end_at_no_overlap` |
|
||||
|
|
|
|||
|
|
@ -259,15 +259,12 @@ and therefore it does not have any records yet.
|
|||
|
||||
## Naming conventions
|
||||
|
||||
Names for database objects (such as tables, indexes, and views) must be lowercase.
|
||||
Lowercase names ensure that queries with unquoted names don't cause errors.
|
||||
|
||||
We keep column names consistent with [ActiveRecord's schema conventions](https://guides.rubyonrails.org/active_record_basics.html#schema-conventions).
|
||||
|
||||
Custom index names should follow the pattern `index_#{table_name}_on_#{column_1}_and_#{column_2}_#{condition}`.
|
||||
|
||||
Examples:
|
||||
|
||||
- `index_services_on_type_and_id_and_template_when_active`
|
||||
- `index_projects_on_id_service_desk_enabled`
|
||||
- `index_clusters_on_enabled_cluster_type_id_and_created_at`
|
||||
Custom index and constraint names should follow the [constraint naming convention guidelines](database/constraint_naming_convention.md).
|
||||
|
||||
### Truncate long index names
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ module Gitlab
|
|||
# Only consider indexes beyond this size (before reindexing)
|
||||
INDEX_SIZE_MINIMUM = 1.gigabyte
|
||||
|
||||
VERY_LARGE_TABLES = %i[
|
||||
ci_builds
|
||||
].freeze
|
||||
|
||||
delegate :each, to: :indexes
|
||||
|
||||
def initialize(candidates)
|
||||
|
|
@ -30,13 +34,24 @@ module Gitlab
|
|||
# we force a N+1 pattern here and estimate bloat on a per-index
|
||||
# basis.
|
||||
|
||||
@indexes ||= candidates
|
||||
.not_recently_reindexed
|
||||
.where('ondisk_size_bytes >= ?', INDEX_SIZE_MINIMUM)
|
||||
@indexes ||= relations_that_need_cleaning_before_deadline
|
||||
.sort_by(&:relative_bloat_level) # forced N+1
|
||||
.reverse
|
||||
.select { |candidate| candidate.relative_bloat_level >= MINIMUM_RELATIVE_BLOAT }
|
||||
end
|
||||
|
||||
def relations_that_need_cleaning_before_deadline
|
||||
relation = candidates.not_recently_reindexed.where('ondisk_size_bytes >= ?', INDEX_SIZE_MINIMUM)
|
||||
relation = relation.where.not(tablename: VERY_LARGE_TABLES) if too_late_for_very_large_table?
|
||||
relation
|
||||
end
|
||||
|
||||
# The reindexing process takes place during the weekends and starting a
|
||||
# reindexing action on a large table late on Sunday could span during
|
||||
# Monday. We don't want this because it prevents vacuum from running.
|
||||
def too_late_for_very_large_table?
|
||||
Date.today.sunday?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def different_version?(version)
|
||||
Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
|
||||
Gitlab::VersionInfo.parse(version) != Gitlab::VersionInfo.parse(Gitlab::ImportExport.version)
|
||||
rescue StandardError => e
|
||||
Gitlab::Import::Logger.error(
|
||||
message: 'Import error',
|
||||
|
|
|
|||
|
|
@ -7,11 +7,14 @@ module Gitlab
|
|||
attr_reader :major, :minor, :patch
|
||||
|
||||
VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)/.freeze
|
||||
# To mitigate ReDoS, limit the length of the version string we're
|
||||
# willing to check
|
||||
MAX_VERSION_LENGTH = 128
|
||||
|
||||
def self.parse(str, parse_suffix: false)
|
||||
if str.is_a?(self)
|
||||
str
|
||||
elsif str && m = str.match(VERSION_REGEX)
|
||||
elsif str && str.length <= MAX_VERSION_LENGTH && m = str.match(VERSION_REGEX)
|
||||
VersionInfo.new(m[1].to_i, m[2].to_i, m[3].to_i, parse_suffix ? m.post_match : nil)
|
||||
else
|
||||
VersionInfo.new
|
||||
|
|
|
|||
|
|
@ -1,86 +1,88 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Feature flag handler sanity checks', :sanity_feature_flags do
|
||||
context 'with an existing feature flag definition file' do
|
||||
let(:definition) do
|
||||
path = Pathname.new('../config/feature_flags')
|
||||
.expand_path(Runtime::Path.qa_root)
|
||||
.glob('**/*.yml')
|
||||
.first
|
||||
YAML.safe_load(File.read(path))
|
||||
RSpec.describe 'Framework sanity', :sanity_feature_flags do
|
||||
describe 'Feature flag handler checks' do
|
||||
context 'with an existing feature flag definition file' do
|
||||
let(:definition) do
|
||||
path = Pathname.new('../config/feature_flags')
|
||||
.expand_path(Runtime::Path.qa_root)
|
||||
.glob('**/*.yml')
|
||||
.first
|
||||
YAML.safe_load(File.read(path))
|
||||
end
|
||||
|
||||
it 'reads the correct default enabled state' do
|
||||
# This test will fail if we ever remove all the feature flags, but that's very unlikely given how many there
|
||||
# are and how much we rely on them.
|
||||
expect(QA::Runtime::Feature.enabled?(definition['name'])).to be definition['default_enabled']
|
||||
end
|
||||
end
|
||||
|
||||
it 'reads the correct default enabled state' do
|
||||
# This test will fail if we ever remove all the feature flags, but that's very unlikely given how many there
|
||||
# are and how much we rely on them.
|
||||
expect(QA::Runtime::Feature.enabled?(definition['name'])).to be definition['default_enabled']
|
||||
end
|
||||
end
|
||||
describe 'feature flag definition files' do
|
||||
let(:file) do
|
||||
path = Pathname.new("#{root}/config/feature_flags/development").expand_path(Runtime::Path.qa_root)
|
||||
path.mkpath
|
||||
Tempfile.new(%w[ff-test .yml], path)
|
||||
end
|
||||
|
||||
describe 'feature flag definition files' do
|
||||
let(:file) do
|
||||
path = Pathname.new("#{root}/config/feature_flags/development").expand_path(Runtime::Path.qa_root)
|
||||
path.mkpath
|
||||
Tempfile.new(%w[ff-test .yml], path)
|
||||
end
|
||||
let(:flag) { Pathname.new(file.path).basename('.yml').to_s }
|
||||
let(:root) { '..' }
|
||||
|
||||
let(:flag) { Pathname.new(file.path).basename('.yml').to_s }
|
||||
let(:root) { '..' }
|
||||
|
||||
before do
|
||||
definition = <<~YAML
|
||||
before do
|
||||
definition = <<~YAML
|
||||
name: #{flag}
|
||||
type: development
|
||||
default_enabled: #{flag_enabled}
|
||||
YAML
|
||||
File.write(file, definition)
|
||||
end
|
||||
YAML
|
||||
File.write(file, definition)
|
||||
end
|
||||
|
||||
after do
|
||||
file.close!
|
||||
end
|
||||
after do
|
||||
file.close!
|
||||
end
|
||||
|
||||
shared_examples 'gets flag value' do
|
||||
context 'with a default disabled feature flag' do
|
||||
let(:flag_enabled) { 'false' }
|
||||
shared_examples 'gets flag value' do
|
||||
context 'with a default disabled feature flag' do
|
||||
let(:flag_enabled) { 'false' }
|
||||
|
||||
it 'reads the flag as disabled' do
|
||||
expect(QA::Runtime::Feature.enabled?(flag)).to be false
|
||||
it 'reads the flag as disabled' do
|
||||
expect(QA::Runtime::Feature.enabled?(flag)).to be false
|
||||
end
|
||||
|
||||
it 'reads as enabled after the flag is enabled' do
|
||||
QA::Runtime::Feature.enable(flag)
|
||||
|
||||
expect { QA::Runtime::Feature.enabled?(flag) }.to eventually_be_truthy
|
||||
.within(max_duration: 60, sleep_interval: 5)
|
||||
end
|
||||
end
|
||||
|
||||
it 'reads as enabled after the flag is enabled' do
|
||||
QA::Runtime::Feature.enable(flag)
|
||||
context 'with a default enabled feature flag' do
|
||||
let(:flag_enabled) { 'true' }
|
||||
|
||||
expect { QA::Runtime::Feature.enabled?(flag) }.to eventually_be_truthy
|
||||
.within(max_duration: 60, sleep_interval: 5)
|
||||
it 'reads the flag as enabled' do
|
||||
expect(QA::Runtime::Feature.enabled?(flag)).to be true
|
||||
end
|
||||
|
||||
it 'reads as disabled after the flag is disabled' do
|
||||
QA::Runtime::Feature.disable(flag)
|
||||
|
||||
expect { QA::Runtime::Feature.enabled?(flag) }.to eventually_be_falsey
|
||||
.within(max_duration: 60, sleep_interval: 5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a default enabled feature flag' do
|
||||
let(:flag_enabled) { 'true' }
|
||||
|
||||
it 'reads the flag as enabled' do
|
||||
expect(QA::Runtime::Feature.enabled?(flag)).to be true
|
||||
end
|
||||
|
||||
it 'reads as disabled after the flag is disabled' do
|
||||
QA::Runtime::Feature.disable(flag)
|
||||
|
||||
expect { QA::Runtime::Feature.enabled?(flag) }.to eventually_be_falsey
|
||||
.within(max_duration: 60, sleep_interval: 5)
|
||||
end
|
||||
context 'with a CE feature flag' do
|
||||
include_examples 'gets flag value'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a CE feature flag' do
|
||||
include_examples 'gets flag value'
|
||||
end
|
||||
context 'with an EE feature flag' do
|
||||
let(:root) { '../ee' }
|
||||
|
||||
context 'with an EE feature flag' do
|
||||
let(:root) { '../ee' }
|
||||
|
||||
include_examples 'gets flag value'
|
||||
include_examples 'gets flag value'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Framework sanity checks', :orchestrated, :framework do
|
||||
RSpec.describe 'Framework sanity', :orchestrated, :framework do
|
||||
describe 'Passing orchestrated example' do
|
||||
it 'succeeds' do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
|
|
|
|||
|
|
@ -1,39 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Browser request interception', :orchestrated, :framework do
|
||||
before(:context) do
|
||||
skip 'Only can test for chrome' unless QA::Runtime::Env.can_intercept?
|
||||
end
|
||||
RSpec.describe 'Framework sanity', :orchestrated, :framework do
|
||||
describe 'Browser request interception' do
|
||||
before(:context) do
|
||||
skip 'Only can test for chrome' unless QA::Runtime::Env.can_intercept?
|
||||
end
|
||||
|
||||
before do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
end
|
||||
before do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
end
|
||||
|
||||
let(:page) { Capybara.current_session }
|
||||
let(:logger) { class_double('QA::Runtime::Logger') }
|
||||
let(:page) { Capybara.current_session }
|
||||
let(:logger) { class_double('QA::Runtime::Logger') }
|
||||
|
||||
it 'intercepts failed graphql calls' do
|
||||
page.execute_script <<~JS
|
||||
it 'intercepts failed graphql calls' do
|
||||
page.execute_script <<~JS
|
||||
fetch('/api/graphql', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: 'query {}'}),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
})
|
||||
JS
|
||||
JS
|
||||
|
||||
Support::Waiter.wait_until do
|
||||
!get_cached_error.nil?
|
||||
Support::Waiter.wait_until do
|
||||
!get_cached_error.nil?
|
||||
end
|
||||
expect(**get_cached_error).to include({ 'method' => 'POST', 'status' => 200, 'url' => '/api/graphql' })
|
||||
end
|
||||
expect(**get_cached_error).to include({ 'method' => 'POST', 'status' => 200, 'url' => '/api/graphql' })
|
||||
end
|
||||
|
||||
def get_cached_error
|
||||
cache = page.execute_script <<~JS
|
||||
def get_cached_error
|
||||
cache = page.execute_script <<~JS
|
||||
return Interceptor.getCache()
|
||||
JS
|
||||
JS
|
||||
|
||||
cache['errors']&.first
|
||||
cache['errors']&.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,31 +7,33 @@ module QA
|
|||
# environment variable is the version actually running.
|
||||
#
|
||||
# See https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1179
|
||||
RSpec.describe 'Version sanity check', :smoke, only: { pipeline: [:pre, :release] } do
|
||||
let(:api_client) { Runtime::API::Client.new(:gitlab) }
|
||||
let(:request) { Runtime::API::Request.new(api_client, '/version') }
|
||||
RSpec.describe 'Framework sanity', :smoke, only: { pipeline: [:pre, :release] } do
|
||||
describe 'Version check' do
|
||||
let(:api_client) { Runtime::API::Client.new(:gitlab) }
|
||||
let(:request) { Runtime::API::Request.new(api_client, '/version') }
|
||||
|
||||
it 'is the specified version' do
|
||||
# The `DEPLOY_VERSION` variable will only be provided for deploys to the
|
||||
# `pre` and `release` environments, which only receive packaged releases.
|
||||
#
|
||||
# For these releases, `deploy_version` will be a package string (e.g.,
|
||||
# `13.1.3-ee.0`), and the reported version will be something like
|
||||
# `13.1.3-ee`, so we only compare the leading SemVer string.
|
||||
#
|
||||
# | Package | Version |
|
||||
# | ---------------- | -------------- |
|
||||
# | 13.3.5-ee.0 | 13.3.5-ee |
|
||||
# | 13.3.0-rc42.ee.0 | 13.3.0-rc42-ee |
|
||||
deploy = Runtime::Env.deploy_version&.gsub(/\A(\d+\.\d+\.\d+).*\z/, '\1')
|
||||
it 'is the specified version' do
|
||||
# The `DEPLOY_VERSION` variable will only be provided for deploys to the
|
||||
# `pre` and `release` environments, which only receive packaged releases.
|
||||
#
|
||||
# For these releases, `deploy_version` will be a package string (e.g.,
|
||||
# `13.1.3-ee.0`), and the reported version will be something like
|
||||
# `13.1.3-ee`, so we only compare the leading SemVer string.
|
||||
#
|
||||
# | Package | Version |
|
||||
# | ---------------- | -------------- |
|
||||
# | 13.3.5-ee.0 | 13.3.5-ee |
|
||||
# | 13.3.0-rc42.ee.0 | 13.3.0-rc42-ee |
|
||||
deploy = Runtime::Env.deploy_version&.gsub(/\A(\d+\.\d+\.\d+).*\z/, '\1')
|
||||
|
||||
skip('No deploy version provided') if deploy.nil? || deploy.empty?
|
||||
skip('No deploy version provided') if deploy.nil? || deploy.empty?
|
||||
|
||||
get request.url
|
||||
get request.url
|
||||
|
||||
expect_status(200)
|
||||
expect(json_body).to have_key(:version)
|
||||
expect(json_body[:version]).to start_with(deploy)
|
||||
expect_status(200)
|
||||
expect(json_body).to have_key(:version)
|
||||
expect(json_body[:version]).to start_with(deploy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@
|
|||
module QA
|
||||
module Support
|
||||
module Formatters
|
||||
# RSpec formatter to enhance metadata present in allure report
|
||||
# Following additional data is added:
|
||||
# * quarantine issue links
|
||||
# * failure issues search link
|
||||
# * ci job link
|
||||
# * flaky status and test pass rate
|
||||
# * devops stage and group as epic and feature behaviour tags
|
||||
#
|
||||
class AllureMetadataFormatter < ::RSpec::Core::Formatters::BaseFormatter
|
||||
include Support::InfluxdbTools
|
||||
|
||||
|
|
@ -18,8 +26,6 @@ module QA
|
|||
# @param [RSpec::Core::Notifications::StartNotification] _start_notification
|
||||
# @return [void]
|
||||
def start(_start_notification)
|
||||
return unless merge_request_iid # on main runs allure native history has pass rate already
|
||||
|
||||
save_flaky_specs
|
||||
log(:debug, "Fetched #{flaky_specs.length} flaky testcases!")
|
||||
rescue StandardError => e
|
||||
|
|
@ -63,11 +69,11 @@ module QA
|
|||
# @param [RSpec::Core::Example] example
|
||||
# @return [void]
|
||||
def add_failure_issues_link(example)
|
||||
spec_file = example.file_path.split('/').last
|
||||
example.issue(
|
||||
'Failure issues',
|
||||
"https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{spec_file}"
|
||||
)
|
||||
return unless example.execution_result.status == :failed
|
||||
|
||||
search_query = ERB::Util.url_encode("Failure in #{example.file_path.gsub('./qa/specs/features/', '')}")
|
||||
search_url = "https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=#{search_query}"
|
||||
example.issue('Failure issues', search_url)
|
||||
rescue StandardError => e
|
||||
log(:error, "Failed to add failure issue link for example '#{example.description}', error: #{e}")
|
||||
end
|
||||
|
|
@ -89,10 +95,10 @@ module QA
|
|||
# @param [RSpec::Core::Example] example
|
||||
# @return [void]
|
||||
def set_flaky_status(example)
|
||||
return unless merge_request_iid && flaky_specs.key?(example.metadata[:testcase])
|
||||
return unless flaky_specs.key?(example.metadata[:testcase]) && example.execution_result.status != :pending
|
||||
|
||||
example.set_flaky
|
||||
example.parameter("pass_rate", "#{flaky_specs[example.metadata[:testcase]].round(1)}%")
|
||||
example.parameter("pass_rate", "#{flaky_specs[example.metadata[:testcase]].round(0)}%")
|
||||
log(:debug, "Setting spec as flaky because it's pass rate is below 98%")
|
||||
rescue StandardError => e
|
||||
log(:error, "Failed to add spec pass rate data for example '#{example.description}', error: #{e}")
|
||||
|
|
|
|||
|
|
@ -5,25 +5,33 @@ describe QA::Support::Formatters::AllureMetadataFormatter do
|
|||
|
||||
let(:formatter) { described_class.new(StringIO.new) }
|
||||
|
||||
let(:rspec_example_notification) { double('RSpec::Core::Notifications::ExampleNotification', example: rspec_example) }
|
||||
let(:rspec_example_notification) do
|
||||
instance_double(RSpec::Core::Notifications::ExampleNotification, example: rspec_example)
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/VerifiedDoubles
|
||||
let(:rspec_example) do
|
||||
double(
|
||||
'RSpec::Core::Example',
|
||||
RSpec::Core::Example,
|
||||
tms: nil,
|
||||
issue: nil,
|
||||
add_link: nil,
|
||||
set_flaky: nil,
|
||||
parameter: nil,
|
||||
attempts: 0,
|
||||
file_path: 'file/path/spec.rb',
|
||||
execution_result: instance_double("RSpec::Core::Example::ExecutionResult", status: :passed),
|
||||
file_path: 'spec.rb',
|
||||
execution_result: instance_double(RSpec::Core::Example::ExecutionResult, status: status),
|
||||
metadata: {
|
||||
testcase: 'testcase',
|
||||
quarantine: { issue: 'issue' }
|
||||
}
|
||||
)
|
||||
end
|
||||
# rubocop:enable RSpec/VerifiedDoubles
|
||||
|
||||
let(:ci_job) { 'ee:relative 5' }
|
||||
let(:ci_job_url) { 'url' }
|
||||
let(:status) { :failed }
|
||||
|
||||
before do
|
||||
stub_env('CI', 'true')
|
||||
|
|
@ -31,16 +39,62 @@ describe QA::Support::Formatters::AllureMetadataFormatter do
|
|||
stub_env('CI_JOB_URL', ci_job_url)
|
||||
end
|
||||
|
||||
it "adds additional data to report" do
|
||||
formatter.example_finished(rspec_example_notification)
|
||||
context 'with links' do
|
||||
it 'adds quarantine, failure issue and ci job links', :aggregate_failures do
|
||||
formatter.example_finished(rspec_example_notification)
|
||||
|
||||
aggregate_failures do
|
||||
expect(rspec_example).to have_received(:issue).with('Quarantine issue', 'issue')
|
||||
expect(rspec_example).to have_received(:add_link).with(name: "Job(#{ci_job})", url: ci_job_url)
|
||||
expect(rspec_example).to have_received(:issue).with(
|
||||
'Failure issues',
|
||||
'https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=spec.rb'
|
||||
'https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&search=Failure%20in%20spec.rb'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with flaky test data', :aggregate_failures do
|
||||
let(:influx_client) { instance_double(InfluxDB2::Client, create_query_api: influx_query_api) }
|
||||
let(:influx_query_api) { instance_double(InfluxDB2::QueryApi, query: data) }
|
||||
let(:data) do
|
||||
[
|
||||
instance_double(
|
||||
InfluxDB2::FluxTable,
|
||||
records: [
|
||||
instance_double(InfluxDB2::FluxRecord, values: { 'status' => 'failed', 'testcase' => 'testcase' }),
|
||||
instance_double(InfluxDB2::FluxRecord, values: { 'status' => 'passed', 'testcase' => 'testcase' })
|
||||
]
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
before do
|
||||
stub_env('QA_RUN_TYPE', 'package-and-test')
|
||||
stub_env('QA_INFLUXDB_URL', 'url')
|
||||
stub_env('QA_INFLUXDB_TOKEN', 'token')
|
||||
|
||||
allow(InfluxDB2::Client).to receive(:new) { influx_client }
|
||||
end
|
||||
|
||||
context 'with non skipped spec' do
|
||||
it 'adds flaky test data' do
|
||||
formatter.start(nil)
|
||||
formatter.example_finished(rspec_example_notification)
|
||||
|
||||
expect(rspec_example).to have_received(:set_flaky)
|
||||
expect(rspec_example).to have_received(:parameter).with('pass_rate', '50%')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with skipped spec' do
|
||||
let(:status) { :pending }
|
||||
|
||||
it 'skips adding flaky test data' do
|
||||
formatter.start(nil)
|
||||
formatter.example_finished(rspec_example_notification)
|
||||
|
||||
expect(rspec_example).not_to have_received(:set_flaky)
|
||||
expect(rspec_example).not_to have_received(:parameter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const {
|
|||
decorateExtractorWithHelpers,
|
||||
} = require('gettext-extractor-vue');
|
||||
const vue2TemplateCompiler = require('vue-template-compiler');
|
||||
const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line');
|
||||
const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line.cjs');
|
||||
|
||||
const args = argumentsParser
|
||||
.option('-f, --file <file>', 'Extract message from one single file')
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ FactoryBot.define do
|
|||
allow_descendants_override_disabled_shared_runners { false }
|
||||
end
|
||||
|
||||
trait :disabled_with_override do
|
||||
trait :disabled_and_overridable do
|
||||
shared_runners_disabled
|
||||
allow_descendants_override_disabled_shared_runners
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ jest.mock('~/api/groups_api');
|
|||
const GROUP_ID = '99';
|
||||
const RUNNER_ENABLED_VALUE = 'enabled';
|
||||
const RUNNER_DISABLED_VALUE = 'disabled_and_unoverridable';
|
||||
const RUNNER_ALLOW_OVERRIDE_VALUE = 'disabled_with_override';
|
||||
const RUNNER_ALLOW_OVERRIDE_VALUE = 'disabled_and_overridable';
|
||||
|
||||
describe('group_settings/components/shared_runners_form', () => {
|
||||
let wrapper;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import ensureSingleLine from '~/locale/ensure_single_line';
|
||||
import ensureSingleLine from '~/locale/ensure_single_line.cjs';
|
||||
|
||||
describe('locale', () => {
|
||||
describe('ensureSingleLine', () => {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ RSpec.describe Ci::RunnersHelper do
|
|||
{
|
||||
runner_enabled_value: Namespace::SR_ENABLED,
|
||||
runner_disabled_value: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
|
||||
runner_allow_override_value: Namespace::SR_DISABLED_WITH_OVERRIDE
|
||||
runner_allow_override_value: Namespace::SR_DISABLED_AND_OVERRIDABLE
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ RSpec.describe Ci::RunnersHelper do
|
|||
|
||||
where(:shared_runners_setting, :is_disabled_and_unoverridable) do
|
||||
:shared_runners_enabled | "false"
|
||||
:disabled_with_override | "false"
|
||||
:disabled_and_overridable | "false"
|
||||
:disabled_and_unoverridable | "true"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -74,4 +74,22 @@ RSpec.describe Gitlab::Database::Reindexing::IndexSelection do
|
|||
expect(subject.map(&:name).sort).to eq(not_recently_reindexed.map(&:name).sort)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with restricted tables' do
|
||||
let!(:ci_builds) do
|
||||
create(
|
||||
:postgres_index_bloat_estimate,
|
||||
index: create(:postgres_index, ondisk_size_bytes: 100.gigabytes, tablename: 'ci_builds'),
|
||||
bloat_size_bytes: 20.gigabyte
|
||||
)
|
||||
end
|
||||
|
||||
context 'when executed on Saturdays', time_travel_to: '2022-12-17T09:44:07Z' do
|
||||
it { expect(subject).to include(ci_builds.index) }
|
||||
end
|
||||
|
||||
context 'when executed on Sundays', time_travel_to: '2022-12-18T09:44:07Z' do
|
||||
it { expect(subject).not_to include(ci_builds.index) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::ImportExport::VersionChecker do
|
||||
RSpec.describe Gitlab::ImportExport::VersionChecker, feature_category: :import do
|
||||
include ImportExport::CommonUtil
|
||||
|
||||
let!(:shared) { Gitlab::ImportExport::Shared.new(nil) }
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ RSpec.describe Gitlab::VersionInfo do
|
|||
it { expect(described_class.parse("1.0.0-rc1-ee")).to eq(@v1_0_0) }
|
||||
it { expect(described_class.parse("git 1.0.0b1")).to eq(@v1_0_0) }
|
||||
it { expect(described_class.parse("git 1.0b1")).not_to be_valid }
|
||||
it { expect(described_class.parse("1.1.#{'1' * described_class::MAX_VERSION_LENGTH}")).not_to be_valid }
|
||||
it { expect(described_class.parse(nil)).not_to be_valid }
|
||||
|
||||
context 'with parse_suffix: true' do
|
||||
let(:versions) do
|
||||
|
|
@ -182,4 +184,10 @@ RSpec.describe Gitlab::VersionInfo do
|
|||
it { expect(@v1_0_1.without_patch).to eq(@v1_0_0) }
|
||||
it { expect(@v1_0_1_rc1.without_patch).to eq(@v1_0_0) }
|
||||
end
|
||||
|
||||
describe 'MAX_VERSION_LENGTH' do
|
||||
subject { described_class::MAX_VERSION_LENGTH }
|
||||
|
||||
it { is_expected.to eq(128) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2648,7 +2648,81 @@ RSpec.describe Group do
|
|||
end
|
||||
end
|
||||
|
||||
context 'disabled_with_override' do
|
||||
context 'disabled_and_overridable' do
|
||||
subject { group.update_shared_runners_setting!(Namespace::SR_DISABLED_AND_OVERRIDABLE) }
|
||||
|
||||
context 'top level group' do
|
||||
let_it_be(:group) { create(:group, :shared_runners_disabled) }
|
||||
let_it_be(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
|
||||
let_it_be(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }
|
||||
|
||||
it 'enables allow descendants to override only for itself' do
|
||||
expect { subject_and_reload(group, sub_group, project) }
|
||||
.to change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
|
||||
.and not_change { group.shared_runners_enabled }
|
||||
.and not_change { sub_group.allow_descendants_override_disabled_shared_runners }
|
||||
.and not_change { sub_group.shared_runners_enabled }
|
||||
.and not_change { project.shared_runners_enabled }
|
||||
end
|
||||
end
|
||||
|
||||
context 'group that its ancestors have shared Runners disabled but allows to override' do
|
||||
let_it_be(:parent) { create(:group, :shared_runners_disabled, :allow_descendants_override_disabled_shared_runners) }
|
||||
let_it_be(:group) { create(:group, :shared_runners_disabled, parent: parent) }
|
||||
let_it_be(:project) { create(:project, shared_runners_enabled: false, group: group) }
|
||||
|
||||
it 'enables allow descendants to override' do
|
||||
expect { subject_and_reload(parent, group, project) }
|
||||
.to not_change { parent.allow_descendants_override_disabled_shared_runners }
|
||||
.and not_change { parent.shared_runners_enabled }
|
||||
.and change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
|
||||
.and not_change { group.shared_runners_enabled }
|
||||
.and not_change { project.shared_runners_enabled }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent does not allow' do
|
||||
let_it_be(:parent, reload: true) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false) }
|
||||
let_it_be(:group, reload: true) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent: parent) }
|
||||
|
||||
it 'raises exception' do
|
||||
expect { subject }
|
||||
.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Allow descendants override disabled shared runners cannot be enabled because parent group does not allow it')
|
||||
end
|
||||
|
||||
it 'does not allow descendants to override' do
|
||||
expect do
|
||||
begin
|
||||
subject
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
parent.reload
|
||||
group.reload
|
||||
end.to not_change { parent.allow_descendants_override_disabled_shared_runners }
|
||||
.and not_change { parent.shared_runners_enabled }
|
||||
.and not_change { group.allow_descendants_override_disabled_shared_runners }
|
||||
.and not_change { group.shared_runners_enabled }
|
||||
end
|
||||
end
|
||||
|
||||
context 'top level group that has shared Runners enabled' do
|
||||
let_it_be(:group) { create(:group, shared_runners_enabled: true) }
|
||||
let_it_be(:sub_group) { create(:group, shared_runners_enabled: true, parent: group) }
|
||||
let_it_be(:project) { create(:project, shared_runners_enabled: true, group: sub_group) }
|
||||
|
||||
it 'enables allow descendants to override & disables shared runners everywhere' do
|
||||
expect { subject_and_reload(group, sub_group, project) }
|
||||
.to change { group.shared_runners_enabled }.from(true).to(false)
|
||||
.and change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
|
||||
.and change { sub_group.shared_runners_enabled }.from(true).to(false)
|
||||
.and change { project.shared_runners_enabled }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'disabled_with_override (deprecated)' do
|
||||
subject { group.update_shared_runners_setting!(Namespace::SR_DISABLED_WITH_OVERRIDE) }
|
||||
|
||||
context 'top level group' do
|
||||
|
|
|
|||
|
|
@ -2114,7 +2114,7 @@ RSpec.describe Namespace do
|
|||
where(:shared_runners_enabled, :allow_descendants_override_disabled_shared_runners, :shared_runners_setting) do
|
||||
true | true | Namespace::SR_ENABLED
|
||||
true | false | Namespace::SR_ENABLED
|
||||
false | true | Namespace::SR_DISABLED_WITH_OVERRIDE
|
||||
false | true | Namespace::SR_DISABLED_AND_OVERRIDABLE
|
||||
false | false | Namespace::SR_DISABLED_AND_UNOVERRIDABLE
|
||||
end
|
||||
|
||||
|
|
@ -2133,12 +2133,15 @@ RSpec.describe Namespace do
|
|||
where(:shared_runners_enabled, :allow_descendants_override_disabled_shared_runners, :other_setting, :result) do
|
||||
true | true | Namespace::SR_ENABLED | false
|
||||
true | true | Namespace::SR_DISABLED_WITH_OVERRIDE | true
|
||||
true | true | Namespace::SR_DISABLED_AND_OVERRIDABLE | true
|
||||
true | true | Namespace::SR_DISABLED_AND_UNOVERRIDABLE | true
|
||||
false | true | Namespace::SR_ENABLED | false
|
||||
false | true | Namespace::SR_DISABLED_WITH_OVERRIDE | false
|
||||
false | true | Namespace::SR_DISABLED_AND_OVERRIDABLE | false
|
||||
false | true | Namespace::SR_DISABLED_AND_UNOVERRIDABLE | true
|
||||
false | false | Namespace::SR_ENABLED | false
|
||||
false | false | Namespace::SR_DISABLED_WITH_OVERRIDE | false
|
||||
false | false | Namespace::SR_DISABLED_AND_OVERRIDABLE | false
|
||||
false | false | Namespace::SR_DISABLED_AND_UNOVERRIDABLE | false
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6637,8 +6637,8 @@ RSpec.describe Project, factory_default: :keep do
|
|||
where(:shared_runners_setting, :project_shared_runners_enabled, :valid_record) do
|
||||
:shared_runners_enabled | true | true
|
||||
:shared_runners_enabled | false | true
|
||||
:disabled_with_override | true | true
|
||||
:disabled_with_override | false | true
|
||||
:disabled_and_overridable | true | true
|
||||
:disabled_and_overridable | false | true
|
||||
:disabled_and_unoverridable | true | false
|
||||
:disabled_and_unoverridable | false | true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ RSpec.describe 'GroupUpdate', feature_category: :subgroups do
|
|||
let(:variables) do
|
||||
{
|
||||
full_path: group.full_path,
|
||||
shared_runners_setting: 'DISABLED_WITH_OVERRIDE'
|
||||
shared_runners_setting: 'DISABLED_AND_OVERRIDABLE'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -52,6 +52,23 @@ RSpec.describe 'GroupUpdate', feature_category: :subgroups do
|
|||
expect(group.reload.shared_runners_setting).to eq(variables[:shared_runners_setting].downcase)
|
||||
end
|
||||
|
||||
context 'when using DISABLED_WITH_OVERRIDE (deprecated)' do
|
||||
let(:variables) do
|
||||
{
|
||||
full_path: group.full_path,
|
||||
shared_runners_setting: 'DISABLED_WITH_OVERRIDE'
|
||||
}
|
||||
end
|
||||
|
||||
it 'updates shared runners settings with disabled_and_overridable' do
|
||||
post_graphql_mutation(mutation, current_user: user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
expect(graphql_errors).to be_nil
|
||||
expect(group.reload.shared_runners_setting).to eq('disabled_and_overridable')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when bad arguments are provided' do
|
||||
let(:variables) { { full_path: '', shared_runners_setting: 'INVALID' } }
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
let(:new_parent_group) { create(:group, shared_runners_enabled: false, allow_descendants_override_disabled_shared_runners: true) }
|
||||
|
||||
it 'calls update service' do
|
||||
expect(Groups::UpdateSharedRunnersService).to receive(:new).with(group, user, { shared_runners_setting: Namespace::SR_DISABLED_WITH_OVERRIDE }).and_call_original
|
||||
expect(Groups::UpdateSharedRunnersService).to receive(:new).with(group, user, { shared_runners_setting: Namespace::SR_DISABLED_AND_OVERRIDABLE }).and_call_original
|
||||
|
||||
transfer_service.execute(new_parent_group)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -114,13 +114,13 @@ RSpec.describe Groups::UpdateSharedRunnersService do
|
|||
end
|
||||
|
||||
context 'allow descendants to override' do
|
||||
let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_WITH_OVERRIDE } }
|
||||
let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_AND_OVERRIDABLE } }
|
||||
|
||||
context 'top level group' do
|
||||
let_it_be(:group) { create(:group, :shared_runners_disabled) }
|
||||
|
||||
it 'receives correct method and succeeds' do
|
||||
expect(group).to receive(:update_shared_runners_setting!).with(Namespace::SR_DISABLED_WITH_OVERRIDE)
|
||||
expect(group).to receive(:update_shared_runners_setting!).with(Namespace::SR_DISABLED_AND_OVERRIDABLE)
|
||||
|
||||
expect(subject[:status]).to eq(:success)
|
||||
end
|
||||
|
|
@ -135,6 +135,30 @@ RSpec.describe Groups::UpdateSharedRunnersService do
|
|||
expect(subject[:message]).to eq('Validation failed: Allow descendants override disabled shared runners cannot be enabled because parent group does not allow it')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using DISABLED_WITH_OVERRIDE (deprecated)' do
|
||||
let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_WITH_OVERRIDE } }
|
||||
|
||||
context 'top level group' do
|
||||
let_it_be(:group) { create(:group, :shared_runners_disabled) }
|
||||
|
||||
it 'receives correct method and succeeds' do
|
||||
expect(group).to receive(:update_shared_runners_setting!).with(Namespace::SR_DISABLED_WITH_OVERRIDE)
|
||||
|
||||
expect(subject[:status]).to eq(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent does not allow' do
|
||||
let_it_be(:parent) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false) }
|
||||
let_it_be(:group) { create(:group, :shared_runners_disabled, allow_descendants_override_disabled_shared_runners: false, parent: parent) }
|
||||
|
||||
it 'results error' do
|
||||
expect(subject[:status]).to eq(:error)
|
||||
expect(subject[:message]).to eq('Validation failed: Allow descendants override disabled shared runners cannot be enabled because parent group does not allow it')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -995,6 +995,7 @@ RSpec.describe Projects::CreateService, '#execute' do
|
|||
where(:shared_runners_setting, :desired_config_for_new_project, :expected_result_for_project) do
|
||||
Namespace::SR_ENABLED | nil | true
|
||||
Namespace::SR_DISABLED_WITH_OVERRIDE | nil | false
|
||||
Namespace::SR_DISABLED_AND_OVERRIDABLE | nil | false
|
||||
Namespace::SR_DISABLED_AND_UNOVERRIDABLE | nil | false
|
||||
end
|
||||
|
||||
|
|
@ -1017,6 +1018,8 @@ RSpec.describe Projects::CreateService, '#execute' do
|
|||
Namespace::SR_ENABLED | false | false
|
||||
Namespace::SR_DISABLED_WITH_OVERRIDE | false | false
|
||||
Namespace::SR_DISABLED_WITH_OVERRIDE | true | true
|
||||
Namespace::SR_DISABLED_AND_OVERRIDABLE | false | false
|
||||
Namespace::SR_DISABLED_AND_OVERRIDABLE | true | true
|
||||
Namespace::SR_DISABLED_AND_UNOVERRIDABLE | false | false
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -373,6 +373,28 @@ RSpec.describe Projects::ImportService do
|
|||
|
||||
expect(result[:status]).to eq(:success)
|
||||
end
|
||||
|
||||
context 'when host resolves to an IPv6 address' do
|
||||
before do
|
||||
project.import_url = 'https://gitlab.com/gitlab-org/gitlab-development-kit'
|
||||
|
||||
allow(Gitlab::UrlBlocker).to receive(:validate!)
|
||||
.with(project.import_url, ports: Project::VALID_IMPORT_PORTS, schemes: Project::VALID_IMPORT_PROTOCOLS, dns_rebind_protection: true)
|
||||
.and_return([Addressable::URI.parse('https://[2606:4700:90:0:f22e:fbec:5bed:a9b9]/gitlab-org/gitlab-development-kit'), 'gitlab.com'])
|
||||
end
|
||||
|
||||
it 'imports repository with url and additional resolved bare IPv6 address' do
|
||||
expect(project.repository).to receive(:import_repository).with('https://gitlab.com/gitlab-org/gitlab-development-kit', resolved_address: '2606:4700:90:0:f22e:fbec:5bed:a9b9').and_return(true)
|
||||
|
||||
expect_next_instance_of(Projects::LfsPointers::LfsImportService) do |service|
|
||||
expect(service).to receive(:execute).and_return(status: :success)
|
||||
end
|
||||
|
||||
result = subject.execute
|
||||
|
||||
expect(result[:status]).to eq(:success)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when http url is provided' do
|
||||
|
|
|
|||
|
|
@ -535,8 +535,8 @@ RSpec.describe Projects::TransferService do
|
|||
where(:project_shared_runners_enabled, :shared_runners_setting, :expected_shared_runners_enabled) do
|
||||
true | :disabled_and_unoverridable | false
|
||||
false | :disabled_and_unoverridable | false
|
||||
true | :disabled_with_override | true
|
||||
false | :disabled_with_override | false
|
||||
true | :disabled_and_overridable | true
|
||||
false | :disabled_and_overridable | false
|
||||
true | :shared_runners_enabled | true
|
||||
false | :shared_runners_enabled | false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Tooling
|
|||
module ConfigFiles
|
||||
SUGGEST_INTRODUCED_BY_COMMENT = <<~SUGGEST_COMMENT
|
||||
```suggestion
|
||||
introduced_by_url: "%<url>s"
|
||||
introduced_by_url: %<url>
|
||||
```
|
||||
SUGGEST_COMMENT
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue