Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-01 06:12:25 +00:00
parent 188f99dcc3
commit 2bfa43cf3a
50 changed files with 2116 additions and 209 deletions

View File

@ -206,13 +206,6 @@ Layout/HashAlignment:
- 'app/uploaders/file_uploader.rb'
- 'app/workers/emails_on_push_worker.rb'
- 'app/workers/x509_issuer_crl_check_worker.rb'
- 'config/initializers/1_settings.rb'
- 'config/initializers/default_url_options.rb'
- 'config/initializers/rest-client-hostname_override.rb'
- 'config/routes/profile.rb'
- 'config/routes/project.rb'
- 'config/routes/unmatched_project.rb'
- 'config/routes/uploads.rb'
- 'db/migrate/20210601080039_group_protected_environments_add_index_and_constraint.rb'
- 'db/migrate/20210804150320_create_base_work_item_types.rb'
- 'db/migrate/20210831203408_upsert_base_work_item_types.rb'

View File

@ -1,30 +0,0 @@
---
# Cop supports --auto-correct.
Performance/ConstantRegexp:
Exclude:
- 'app/models/commit.rb'
- 'app/models/commit_range.rb'
- 'app/models/custom_emoji.rb'
- 'app/models/gpg_key.rb'
- 'app/models/merge_request.rb'
- 'app/models/packages/package.rb'
- 'app/models/project.rb'
- 'app/models/wiki.rb'
- 'ee/app/models/ee/epic.rb'
- 'lib/banzai/filter/custom_emoji_filter.rb'
- 'lib/gitlab/cleanup/project_uploads.rb'
- 'lib/gitlab/database/reindexing/reindex_concurrently.rb'
- 'lib/gitlab/dependency_linker/base_linker.rb'
- 'lib/gitlab/dependency_linker/composer_json_linker.rb'
- 'lib/gitlab/dependency_linker/godeps_json_linker.rb'
- 'lib/gitlab/dependency_linker/podspec_linker.rb'
- 'lib/gitlab/git.rb'
- 'lib/gitlab/job_waiter.rb'
- 'lib/gitlab/metrics/dashboard/url.rb'
- 'lib/gitlab/path_regex.rb'
- 'lib/gitlab/regex.rb'
- 'scripts/perf/query_limiting_report.rb'
- 'scripts/validate_migration_schema'
- 'spec/features/users/email_verification_on_login_spec.rb'
- 'spec/models/concerns/token_authenticatable_spec.rb'
- 'spec/services/notes/copy_service_spec.rb'

View File

@ -16,12 +16,9 @@ import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __ } from '~/locale';
import {
VISIBILITY_TYPE_ICON,
GROUP_VISIBILITY_TYPE,
ITEM_TYPE,
VISIBILITY_PRIVATE,
} from '../constants';
import { VISIBILITY_LEVELS_ENUM } from '~/visibility_level/constants';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, ITEM_TYPE } from '../constants';
import eventHub from '../event_hub';
import itemActions from './item_actions.vue';
@ -114,8 +111,8 @@ export default {
shouldShowVisibilityWarning() {
return (
this.action === 'shared' &&
this.currentGroupVisibility === VISIBILITY_PRIVATE &&
this.group.visibility !== VISIBILITY_PRIVATE
VISIBILITY_LEVELS_ENUM[this.group.visibility] >
VISIBILITY_LEVELS_ENUM[this.currentGroupVisibility]
);
},
},
@ -142,7 +139,7 @@ export default {
shareProjectsWithGroupsHelpPagePath: helpPagePath(
'user/project/members/share_project_with_groups',
{
anchor: 'share-a-public-project-with-private-group',
anchor: 'sharing-projects-with-groups-of-a-higher-restrictive-visibility-level',
},
),
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },

View File

@ -1,4 +1,9 @@
import { __, s__ } from '~/locale';
import {
VISIBILITY_LEVEL_PRIVATE,
VISIBILITY_LEVEL_INTERNAL,
VISIBILITY_LEVEL_PUBLIC,
} from '~/visibility_level/constants';
export const MAX_CHILDREN_COUNT = 20;
@ -28,32 +33,30 @@ export const ITEM_TYPE = {
GROUP: 'group',
};
export const VISIBILITY_PUBLIC = 'public';
export const VISIBILITY_INTERNAL = 'internal';
export const VISIBILITY_PRIVATE = 'private';
export const GROUP_VISIBILITY_TYPE = {
[VISIBILITY_PUBLIC]: __(
[VISIBILITY_LEVEL_PUBLIC]: __(
'Public - The group and any public projects can be viewed without any authentication.',
),
[VISIBILITY_INTERNAL]: __(
[VISIBILITY_LEVEL_INTERNAL]: __(
'Internal - The group and any internal projects can be viewed by any logged in user except external users.',
),
[VISIBILITY_PRIVATE]: __('Private - The group and its projects can only be viewed by members.'),
[VISIBILITY_LEVEL_PRIVATE]: __(
'Private - The group and its projects can only be viewed by members.',
),
};
export const PROJECT_VISIBILITY_TYPE = {
[VISIBILITY_PUBLIC]: __('Public - The project can be accessed without any authentication.'),
[VISIBILITY_INTERNAL]: __(
[VISIBILITY_LEVEL_PUBLIC]: __('Public - The project can be accessed without any authentication.'),
[VISIBILITY_LEVEL_INTERNAL]: __(
'Internal - The project can be accessed by any logged in user except external users.',
),
[VISIBILITY_PRIVATE]: __(
[VISIBILITY_LEVEL_PRIVATE]: __(
'Private - Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.',
),
};
export const VISIBILITY_TYPE_ICON = {
[VISIBILITY_PUBLIC]: 'earth',
[VISIBILITY_INTERNAL]: 'shield',
[VISIBILITY_PRIVATE]: 'lock',
[VISIBILITY_LEVEL_PUBLIC]: 'earth',
[VISIBILITY_LEVEL_INTERNAL]: 'shield',
[VISIBILITY_LEVEL_PRIVATE]: 'lock',
};

View File

@ -0,0 +1,10 @@
export const VISIBILITY_LEVEL_PRIVATE = 'private';
export const VISIBILITY_LEVEL_INTERNAL = 'internal';
export const VISIBILITY_LEVEL_PUBLIC = 'public';
// Matches `lib/gitlab/visibility_level.rb`
export const VISIBILITY_LEVELS_ENUM = {
[VISIBILITY_LEVEL_PRIVATE]: 0,
[VISIBILITY_LEVEL_INTERNAL]: 10,
[VISIBILITY_LEVEL_PUBLIC]: 20,
};

View File

@ -190,7 +190,7 @@ class Commit
def self.link_reference_pattern
@link_reference_pattern ||=
super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})?(\.(?<extension>#{LINK_EXTENSION_PATTERN}))?/)
super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})?(\.(?<extension>#{LINK_EXTENSION_PATTERN}))?/o)
end
def to_reference(from = nil, full: false)

View File

@ -50,7 +50,7 @@ class CommitRange
end
def self.link_reference_pattern
@link_reference_pattern ||= super("compare", /(?<commit_range>#{PATTERN})/)
@link_reference_pattern ||= super("compare", /(?<commit_range>#{PATTERN})/o)
end
# Initialize a CommitRange
@ -64,7 +64,7 @@ class CommitRange
range_string = range_string.strip
unless range_string =~ /\A#{PATTERN}\z/
unless range_string =~ /\A#{PATTERN}\z/o
raise ArgumentError, "invalid CommitRange string format: #{range_string}"
end

View File

@ -22,7 +22,7 @@ class CustomEmoji < ApplicationRecord
presence: true,
length: { maximum: 36 },
format: { with: /\A#{NAME_REGEXP}\z/ }
format: { with: /\A#{NAME_REGEXP}\z/o }
scope :by_name, -> (names) { where(name: names) }

View File

@ -21,7 +21,7 @@ class GpgKey < ApplicationRecord
presence: true,
uniqueness: true,
format: {
with: /\A#{KEY_PREFIX}((?!#{KEY_PREFIX})(?!#{KEY_SUFFIX}).)+#{KEY_SUFFIX}\Z/m,
with: /\A#{KEY_PREFIX}((?!#{KEY_PREFIX})(?!#{KEY_SUFFIX}).)+#{KEY_SUFFIX}\Z/mo,
message: "is invalid. A valid public GPG key begins with '#{KEY_PREFIX}' and ends with '#{KEY_SUFFIX}'"
}

View File

@ -1500,7 +1500,7 @@ class MergeRequest < ApplicationRecord
end
def self.merge_train_ref?(ref)
%r{\Arefs/#{Repository::REF_MERGE_REQUEST}/\d+/train\z}.match?(ref)
%r{\Arefs/#{Repository::REF_MERGE_REQUEST}/\d+/train\z}o.match?(ref)
end
def in_locked_state

View File

@ -327,7 +327,7 @@ class Packages::Package < ApplicationRecord
def normalized_pypi_name
return name unless pypi?
name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/, '-').downcase
name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/o, '-').downcase
end
private

View File

@ -814,7 +814,7 @@ class Project < ApplicationRecord
(?<!#{Gitlab::PathRegex::PATH_START_CHAR})
((?<namespace>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})\/)?
(?<project>#{Gitlab::PathRegex::PROJECT_PATH_FORMAT_REGEX})
}x
}xo
end
def reference_postfix

View File

@ -335,7 +335,7 @@ class Wiki
end
def wiki_base_path
web_url(only_path: true).sub(%r{/#{Wiki::HOMEPAGE}\z}, '')
web_url(only_path: true).sub(%r{/#{Wiki::HOMEPAGE}\z}o, '')
end
# Callbacks for synchronous processing after wiki changes.

File diff suppressed because it is too large Load Diff

View File

@ -136,9 +136,9 @@ if github_settings
OmniAuth::Strategies::GitHub.default_options[:client_options]
else
{
"site" => File.join(github_settings["url"], "api/v3"),
"site" => File.join(github_settings["url"], "api/v3"),
"authorize_url" => File.join(github_settings["url"], "login/oauth/authorize"),
"token_url" => File.join(github_settings["url"], "login/oauth/access_token")
"token_url" => File.join(github_settings["url"], "login/oauth/access_token")
}
end
end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
default_url_options = {
host: Gitlab.config.gitlab.host,
protocol: Gitlab.config.gitlab.protocol,
host: Gitlab.config.gitlab.host,
protocol: Gitlab.config.gitlab.protocol,
script_name: Gitlab.config.gitlab.relative_url_root
}

View File

@ -8,8 +8,8 @@ module RestClient
def transmit(uri, req, payload, &block)
begin
ip, hostname_override = Gitlab::UrlBlocker.validate!(uri, allow_local_network: allow_settings_local_requests?,
allow_localhost: allow_settings_local_requests?,
dns_rebind_protection: dns_rebind_protection?)
allow_localhost: allow_settings_local_requests?,
dns_rebind_protection: dns_rebind_protection?)
self.hostname_override = hostname_override
rescue Gitlab::UrlBlocker::BlockedUrlError => e

View File

@ -23,10 +23,10 @@ resource :profile, only: [:show, :update] do
resource :notifications, only: [:show, :update] do
scope(path: 'groups/*id',
id: Gitlab::PathRegex.full_namespace_route_regex,
as: :group,
controller: :groups,
constraints: { format: /(html|json)/ }) do
id: Gitlab::PathRegex.full_namespace_route_regex,
as: :group,
controller: :groups,
constraints: { format: /(html|json)/ }) do
patch '/', action: :update
put '/', action: :update
end

View File

@ -29,7 +29,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# "Route Globbing" syntax (/*page) so that the route helpers do not encode
# the slash character.
get 'metrics(/:dashboard_path)(/*page)', constraints: { dashboard_path: /.+\.yml/, page: 'panel/new' },
to: 'metrics_dashboard#show', as: :metrics_dashboard, format: false
to: 'metrics_dashboard#show', as: :metrics_dashboard, format: false
namespace :metrics, module: :metrics do
namespace :dashboards do

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
scope(path: '*namespace_id',
as: :namespace,
namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do
as: :namespace,
namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do
scope(path: ':project_id',
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
as: :project) do
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
as: :project) do
post '*all', to: 'application#route_not_found'
put '*all', to: 'application#route_not_found'
patch '*all', to: 'application#route_not_found'

View File

@ -3,8 +3,8 @@
scope path: :uploads do
# Note attachments and User/Group/Project/Topic avatars
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: %r{note|user|group|project|projects\/topic}, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
to: "uploads#show",
constraints: { model: %r{note|user|group|project|projects\/topic}, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
# show uploads for models, snippets (notes) available for now
get '-/system/:model/:id/:secret/:filename',
@ -18,8 +18,8 @@ scope path: :uploads do
# Appearance
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /appearance/, mounted_as: /logo|header_logo|favicon/, filename: /.+/ },
to: "uploads#show",
constraints: { model: /appearance/, mounted_as: /logo|header_logo|favicon/, filename: /.+/ },
as: 'appearance_upload'
# Project markdown uploads
@ -27,7 +27,7 @@ scope path: :uploads do
# https://gitlab.com/gitlab-org/gitlab/issues/196396
get ":namespace_id/:project_id/:secret/:filename",
to: redirect("%{namespace_id}/%{project_id}/uploads/%{secret}/%{filename}"),
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: %r{[^/]+} }, format: false, defaults: { format: nil }
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: %r{[^/]+} }, format: false, defaults: { format: nil }
# create uploads for models, snippets (notes) available for now
post ':model',
@ -41,12 +41,12 @@ scope path: :uploads do
# Alert Metric Images
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /alert_management_metric_image/, mounted_as: /file/, filename: %r{[^/]+} },
to: "uploads#show",
constraints: { model: /alert_management_metric_image/, mounted_as: /file/, filename: %r{[^/]+} },
as: 'alert_metric_image_upload'
end
# Redirect old note attachments path to new uploads path.
get "files/note/:id/:filename",
to: redirect("uploads/note/attachment/%{id}/%{filename}"),
constraints: { filename: %r{[^/]+} }
to: redirect("uploads/note/attachment/%{id}/%{filename}"),
constraints: { filename: %r{[^/]+} }

View File

@ -52,7 +52,7 @@ After sharing 'Project Acme' with 'Engineering':
When you share a project, be aware of the following restrictions and outcomes:
- [Maximum access level](#maximum-access-level)
- [Sharing a public project with a private group](#share-a-public-project-with-private-group)
- [Sharing projects with groups of a higher restrictive visibility level](#sharing-projects-with-groups-of-a-higher-restrictive-visibility-level)
- [Sharing project with group lock](#share-project-with-group-lock)
## Maximum access level
@ -67,13 +67,19 @@ in. That means you can only share down the hierarchy. For example, `group/subgro
- Can not be shared with `group`.
- Can be shared with `group/subgroup02` or `group/subgroup01/subgroup03`.
## Share a public project with private group
## Sharing projects with groups of a higher restrictive visibility level
When you share a public project with a private group, be aware of the following outcomes:
There are several outcomes you must be aware of when you share a project with a group that has a more restrictive [visibility level](../../public_access.md#project-and-group-visibility) than the project. For example, when you:
- The name of the group is no longer private and is visible to all users in the project members page.
- Owners of the project have access to members of the private group when they mention them in issues or merge requests.
- Project members who are direct or indirect members of the private group can see private group members listed in addition to members of the project.
- Share a public project with a private group.
- Share a public project with an internal group.
- Share an internal project with a private group.
The following outcomes occur:
- The group name is visible to all users that can view the project members page.
- Owners of the project have access to members of the group when they mention them in issues or merge requests.
- Project members who are direct or indirect members of the group can see group members listed in addition to members of the project.
## Share project with group lock

View File

@ -29,7 +29,7 @@ module Banzai
@emoji_pattern ||=
/(?<=[^[:alnum:]:]|\n|^)
:(#{CustomEmoji::NAME_REGEXP}):
(?=[^[:alnum:]:]|$)/x
(?=[^[:alnum:]:]|$)/xo
end
def custom_emoji_name_element_filter(text)
@ -58,7 +58,7 @@ module Banzai
end
def custom_emoji_candidates
doc.to_html.scan(/:(#{CustomEmoji::NAME_REGEXP}):/).flatten
doc.to_html.scan(/:(#{CustomEmoji::NAME_REGEXP}):/o).flatten
end
def all_custom_emoji

View File

@ -16,7 +16,7 @@ module Gitlab
def parse!
@data = Gitlab::Json.parse(json_data)
return unless supported_spec_version?
return unless valid?
parse_components
rescue JSON::ParserError => e
@ -27,6 +27,14 @@ module Gitlab
attr_reader :json_data, :report, :data
def schema_validator
@schema_validator ||= Validators::CyclonedxSchemaValidator.new(data)
end
def valid?
valid_schema? && supported_spec_version?
end
def supported_spec_version?
return true if SUPPORTED_SPEC_VERSIONS.include?(data['specVersion'])
@ -38,6 +46,14 @@ module Gitlab
false
end
def valid_schema?
return true if schema_validator.valid?
schema_validator.errors.each { |error| report.add_error(error) }
false
end
def parse_components
data['components']&.each do |component|
next unless supported_component_type?(component['type'])

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Parsers
module Sbom
module Validators
class CyclonedxSchemaValidator
SCHEMA_PATH = Rails.root.join('app', 'validators', 'json_schemas', 'cyclonedx_report.json').freeze
def initialize(report_data)
@report_data = report_data
end
def valid?
errors.empty?
end
def errors
@errors ||= pretty_errors
end
private
def raw_errors
JSONSchemer.schema(SCHEMA_PATH).validate(@report_data)
end
def pretty_errors
raw_errors.map { |error| JSONSchemer::Errors.pretty(error) }
end
end
end
end
end
end
end

View File

@ -56,7 +56,7 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def move_to_lost_and_found(path, dry_run)
new_path = path.sub(/\A#{ProjectUploadFileFinder::ABSOLUTE_UPLOAD_DIR}/, LOST_AND_FOUND)
new_path = path.sub(/\A#{ProjectUploadFileFinder::ABSOLUTE_UPLOAD_DIR}/o, LOST_AND_FOUND)
move(path, new_path, 'move to lost and found', dry_run)
end

View File

@ -20,7 +20,7 @@ module Gitlab
def perform
raise ReindexError, 'indexes serving an exclusion constraint are currently not supported' if index.exclusion?
raise ReindexError, 'index is a left-over temporary index from a previous reindexing run' if index.name =~ /#{TEMPORARY_INDEX_PATTERN}/
raise ReindexError, 'index is a left-over temporary index from a previous reindexing run' if index.name =~ /#{TEMPORARY_INDEX_PATTERN}/o
# Expression indexes require additional statistics in `pg_statistic`:
# select * from pg_statistic where starelid = (select oid from pg_class where relname = 'some_index');
@ -81,10 +81,10 @@ module Gitlab
# Example lingering index name: some_index_ccnew1
# Example prefix: 'some_index'
prefix = lingering_index.name.gsub(/#{TEMPORARY_INDEX_PATTERN}/, '')
prefix = lingering_index.name.gsub(/#{TEMPORARY_INDEX_PATTERN}/o, '')
# Example suffix: '_ccnew1'
suffix = lingering_index.name.match(/#{TEMPORARY_INDEX_PATTERN}/)[0]
suffix = lingering_index.name.match(/#{TEMPORARY_INDEX_PATTERN}/o)[0]
# Only remove if the lingering index name could have been chosen
# as a result of a REINDEX operation (considering that PostgreSQL

View File

@ -34,9 +34,9 @@ module Gitlab
return if external_ref =~ GIT_INVALID_URL_REGEX
case external_ref
when /\A#{URL_REGEX}\z/
when /\A#{URL_REGEX}\z/o
external_ref
when /\A#{REPO_REGEX}\z/
when /\A#{REPO_REGEX}\z/o
github_url(external_ref)
else
package_url(name)

View File

@ -13,7 +13,7 @@ module Gitlab
end
def package_url(name)
"https://packagist.org/packages/#{name}" if name =~ /\A#{REPO_REGEX}\z/
"https://packagist.org/packages/#{name}" if name =~ /\A#{REPO_REGEX}\z/o
end
end
end

View File

@ -12,10 +12,10 @@ module Gitlab
def link_dependencies
link_json('ImportPath') do |path|
case path
when %r{\A(?<repo>github\.com/#{REPO_REGEX})/(?<path>.+)\z}
when %r{\A(?<repo>github\.com/#{REPO_REGEX})/(?<path>.+)\z}o
"https://#{$~[:repo]}/tree/master/#{$~[:path]}"
when %r{\A(?<repo>gitlab\.com/#{NESTED_REPO_REGEX})\.git/(?<path>.+)\z},
%r{\A(?<repo>gitlab\.com/#{REPO_REGEX})/(?<path>.+)\z}
when %r{\A(?<repo>gitlab\.com/#{NESTED_REPO_REGEX})\.git/(?<path>.+)\z}o,
%r{\A(?<repo>gitlab\.com/#{REPO_REGEX})/(?<path>.+)\z}o
"https://#{$~[:repo]}/-/tree/master/#{$~[:path]}"
when /\Agolang\.org/

View File

@ -14,10 +14,10 @@ module Gitlab
def link_dependencies
link_method_call('homepage', URL_REGEX, &:itself)
link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/o, &:itself)
link_method_call('license', &method(:license_url))
link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url))
link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/o, &method(:license_url))
link_method_call('dependency', &method(:package_url))
end

View File

@ -47,11 +47,11 @@ module Gitlab
end
def tag_ref?(ref)
ref =~ /^#{TAG_REF_PREFIX}.+/
ref =~ /^#{TAG_REF_PREFIX}.+/o
end
def branch_ref?(ref)
ref =~ /^#{BRANCH_REF_PREFIX}.+/
ref =~ /^#{BRANCH_REF_PREFIX}.+/o
end
def blank_ref?(ref)

View File

@ -35,7 +35,7 @@ module Gitlab
end
def self.key?(key)
key.is_a?(String) && key =~ /\A#{KEY_PREFIX}:\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/
key.is_a?(String) && key =~ /\A#{KEY_PREFIX}:\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/o
end
attr_reader :key, :finished, :worker_label

View File

@ -42,7 +42,7 @@ module Gitlab
#{DASH_PATTERN}?
/grafana
/metrics_dashboard
}x
}xo
)
end
end
@ -64,7 +64,7 @@ module Gitlab
/(?<cluster_id>\d+)
/?
( (/metrics) | ( /metrics_dashboard\.json ) )?
}x
}xo
)
end
end
@ -82,7 +82,7 @@ module Gitlab
/alerts
/(?<alert>\d+)
/metrics_dashboard(\.json)?
}x
}xo
)
end
end
@ -112,7 +112,7 @@ module Gitlab
/environments
/(?<environment>\d+)
/(metrics_dashboard|metrics)
}x
}xo
end
def non_environment_metrics_regex
@ -125,7 +125,7 @@ module Gitlab
environment=(?<environment>\d+)
.*
)
}x
}xo
end
def regex_for_project_metrics(path_suffix_pattern)

View File

@ -204,7 +204,7 @@ module Gitlab
end
def namespace_format_regex
@namespace_format_regex ||= /\A#{NAMESPACE_FORMAT_REGEX}\z/.freeze
@namespace_format_regex ||= /\A#{NAMESPACE_FORMAT_REGEX}\z/o.freeze
end
def namespace_format_message
@ -213,7 +213,7 @@ module Gitlab
end
def project_path_format_regex
@project_path_format_regex ||= /\A#{PROJECT_PATH_FORMAT_REGEX}\z/.freeze
@project_path_format_regex ||= /\A#{PROJECT_PATH_FORMAT_REGEX}\z/o.freeze
end
def project_path_format_message

View File

@ -70,7 +70,7 @@ module Gitlab
end
def npm_package_name_regex
@npm_package_name_regex ||= %r{\A(?:@(#{Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX})/)?[-+\.\_a-zA-Z0-9]+\z}
@npm_package_name_regex ||= %r{\A(?:@(#{Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX})/)?[-+\.\_a-zA-Z0-9]+\z}o
end
def nuget_package_name_regex
@ -128,15 +128,15 @@ module Gitlab
def debian_architecture_regex
# See official parser: https://git.dpkg.org/cgit/dpkg/dpkg.git/tree/lib/dpkg/arch.c?id=9e0c88ec09475f4d1addde9cdba1ad7849720356#n43
# But we limit to lower case
@debian_architecture_regex ||= %r{\A#{::Packages::Debian::ARCHITECTURE_REGEX}\z}.freeze
@debian_architecture_regex ||= %r{\A#{::Packages::Debian::ARCHITECTURE_REGEX}\z}o.freeze
end
def debian_distribution_regex
@debian_distribution_regex ||= %r{\A#{::Packages::Debian::DISTRIBUTION_REGEX}\z}i.freeze
@debian_distribution_regex ||= %r{\A#{::Packages::Debian::DISTRIBUTION_REGEX}\z}io.freeze
end
def debian_component_regex
@debian_component_regex ||= %r{\A#{::Packages::Debian::COMPONENT_REGEX}\z}.freeze
@debian_component_regex ||= %r{\A#{::Packages::Debian::COMPONENT_REGEX}\z}o.freeze
end
def helm_channel_regex

View File

@ -11497,6 +11497,9 @@ msgstr ""
msgid "CycleAnalyticsStage|should be under a group"
msgstr ""
msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)"
msgstr ""
msgid "CycleAnalytics|'%{name}' is collecting the data. This can take a few minutes."
msgstr ""
@ -11521,6 +11524,9 @@ msgstr ""
msgid "CycleAnalytics|Date"
msgstr ""
msgid "CycleAnalytics|Display chart filters"
msgstr ""
msgid "CycleAnalytics|If you have recently upgraded to GitLab Premium, it can take up to 30 minutes for data to collect and display."
msgstr ""
@ -11530,11 +11536,20 @@ msgstr ""
msgid "CycleAnalytics|Number of tasks"
msgstr ""
msgid "CycleAnalytics|Only %{maxLabels} labels can be selected at this time"
msgstr ""
msgid "CycleAnalytics|Project selected"
msgid_plural "CycleAnalytics|%d projects selected"
msgstr[0] ""
msgstr[1] ""
msgid "CycleAnalytics|Select labels"
msgstr ""
msgid "CycleAnalytics|Show"
msgstr ""
msgid "CycleAnalytics|Stage time: %{title}"
msgstr ""
@ -36286,9 +36301,6 @@ msgstr ""
msgid "Showing version #%{versionNumber}"
msgstr ""
msgid "Shows issues and 1 label for group '%{group_name}' from Nov 1, 2019 to Dec 31, 2019"
msgstr ""
msgid "Shows issues for group '%{group_name}' from Nov 1, 2019 to Dec 31, 2019"
msgstr ""

View File

@ -108,3 +108,5 @@ module QA
end
end
end
QA::Flow::Purchase.prepend_mod_with('Flow::Purchase', namespace: QA)

View File

@ -9,7 +9,7 @@ module QA
end
end
let(:target_group) do
let!(:target_group) do
Resource::Group.fabricate_via_api! do |group|
group.path = "target-group-for-transfer_#{SecureRandom.hex(8)}"
end
@ -19,48 +19,39 @@ module QA
Resource::Project.fabricate_via_api! do |project|
project.group = source_group
project.name = 'transfer-project'
project.initialize_with_readme = true
end
end
let(:edited_readme_content) { 'Here is the edited content.' }
let(:readme_content) { 'Here is the edited content.' }
before do
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.add_files([
{ file_path: 'README.md', content: readme_content }
])
end
Flow::Login.sign_in
project.visit!
Page::Project::Show.perform do |project|
project.click_file('README.md')
end
Page::File::Show.perform(&:click_edit)
Page::File::Edit.perform do |file|
file.remove_content
file.add_content(edited_readme_content)
file.commit_changes
end
end
it 'user transfers a project between groups',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347878' do
# Retry is needed here as the target group is not avaliable for transfer right away.
QA::Support::Retrier.retry_on_exception(reload_page: page) do
Page::File::Show.perform(&:go_to_general_settings)
Page::File::Show.perform(&:go_to_general_settings)
Page::Project::Settings::Main.perform(&:expand_advanced_settings)
Page::Project::Settings::Main.perform(&:expand_advanced_settings)
Page::Project::Settings::Advanced.perform do |advanced|
advanced.transfer_project!(project.name, target_group.full_path)
end
Page::Project::Settings::Advanced.perform do |advanced|
advanced.transfer_project!(project.name, target_group.full_path)
end
Page::Project::Settings::Main.perform(&:click_project)
Page::Project::Show.perform do |project|
expect(project).to have_breadcrumb(target_group.path)
expect(project).to have_readme_content(edited_readme_content)
expect(project).to have_readme_content(readme_content)
end
end
end

View File

@ -124,7 +124,7 @@ class QueryLimitingReport
file_lines.each_index do |index|
line = file_lines[index]
if line =~ /#{CODE_LINES_SEARCH_STRING}/
if line =~ /#{CODE_LINES_SEARCH_STRING}/o
issue_iid = line.slice(%r{issues/(\d+)\D}, 1)
line_number = index + 1
code_line = {

View File

@ -87,7 +87,7 @@ class MigrationSchemaValidator
def find_migration_version(filename)
file_basename = File.basename(filename)
version_match = /\A(?<version>\d{#{VERSION_DIGITS}})_/.match(file_basename)
version_match = /\A(?<version>\d{#{VERSION_DIGITS}})_/o.match(file_basename)
die "#{filename} has an invalid migration version" if version_match.nil?

View File

@ -97,28 +97,43 @@ RSpec.describe 'Group show page' do
end
end
context 'when a public project is shared with a private group' do
let_it_be(:private_group) { create(:group, :private) }
context 'visibility warning popover' do
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:project_group_link) { create(:project_group_link, group: private_group, project: public_project) }
before do
private_group.add_owner(user)
sign_in(user)
shared_examples 'it shows warning popover' do
it 'shows warning popover', :js do
group_to_share_with.add_owner(user)
sign_in(user)
visit group_path(group_to_share_with)
click_link _('Shared projects')
wait_for_requests
page.within("[data-testid=\"group-overview-item-#{public_project.id}\"]") do
click_button _('Less restrictive visibility')
end
expect(page).to have_content _('Project visibility level is less restrictive than the group settings.')
end
end
it 'shows warning popover', :js do
visit group_path(private_group)
click_link _('Shared projects')
wait_for_requests
page.within("[data-testid=\"group-overview-item-#{public_project.id}\"]") do
click_button _('Less restrictive visibility')
context 'when a public project is shared with a private group' do
let_it_be(:group_to_share_with) { create(:group, :private) }
let_it_be(:project_group_link) do
create(:project_group_link, group: group_to_share_with, project: public_project)
end
expect(page).to have_content _('Project visibility level is less restrictive than the group settings.')
include_examples 'it shows warning popover'
end
context 'when a public project is shared with an internal group' do
let_it_be(:group_to_share_with) { create(:group, :internal) }
let_it_be(:project_group_link) do
create(:project_group_link, group: group_to_share_with, project: public_project)
end
include_examples 'it shows warning popover'
end
end

View File

@ -335,7 +335,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
mail = find_email_for(user)
expect(mail.to).to match_array([user.email])
expect(mail.subject).to eq('Verify your identity')
code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/]
code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/o]
reset_delivered_emails!
code
end

View File

@ -6,19 +6,20 @@ import ItemActions from '~/groups/components/item_actions.vue';
import eventHub from '~/groups/event_hub';
import { getGroupItemMicrodata } from '~/groups/store/utils';
import * as urlUtilities from '~/lib/utils/url_utility';
import { ITEM_TYPE } from '~/groups/constants';
import {
ITEM_TYPE,
VISIBILITY_INTERNAL,
VISIBILITY_PRIVATE,
VISIBILITY_PUBLIC,
} from '~/groups/constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
VISIBILITY_LEVEL_PRIVATE,
VISIBILITY_LEVEL_INTERNAL,
VISIBILITY_LEVEL_PUBLIC,
} from '~/visibility_level/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mockParentGroupItem, mockChildren } from '../mock_data';
const createComponent = (
propsData = { group: mockParentGroupItem, parentGroup: mockChildren[0] },
provide = {
currentGroupVisibility: VISIBILITY_PRIVATE,
currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE,
},
) => {
return mountExtended(GroupItem, {
@ -289,7 +290,7 @@ describe('GroupItemComponent', () => {
});
describe('visibility warning popover', () => {
const findPopover = () => wrapper.findComponent(GlPopover);
const findPopover = () => extendedWrapper(wrapper.findComponent(GlPopover));
const itDoesNotRenderVisibilityWarningPopover = () => {
it('does not render visibility warning popover', () => {
@ -319,13 +320,16 @@ describe('GroupItemComponent', () => {
describe('when showing projects', () => {
describe.each`
itemVisibility | currentGroupVisibility | isPopoverShown
${VISIBILITY_PRIVATE} | ${VISIBILITY_PUBLIC} | ${false}
${VISIBILITY_INTERNAL} | ${VISIBILITY_PUBLIC} | ${false}
${VISIBILITY_PUBLIC} | ${VISIBILITY_PUBLIC} | ${false}
${VISIBILITY_PRIVATE} | ${VISIBILITY_PRIVATE} | ${false}
${VISIBILITY_INTERNAL} | ${VISIBILITY_PRIVATE} | ${true}
${VISIBILITY_PUBLIC} | ${VISIBILITY_PRIVATE} | ${true}
itemVisibility | currentGroupVisibility | isPopoverShown
${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_PRIVATE} | ${false}
${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_PRIVATE} | ${true}
${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_PRIVATE} | ${true}
${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_INTERNAL} | ${false}
${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_INTERNAL} | ${false}
${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_INTERNAL} | ${true}
`(
'when item visibility is $itemVisibility and parent group visibility is $currentGroupVisibility',
({ itemVisibility, currentGroupVisibility, isPopoverShown }) => {
@ -347,8 +351,17 @@ describe('GroupItemComponent', () => {
});
if (isPopoverShown) {
it('renders visibility warning popover', () => {
expect(findPopover().exists()).toBe(true);
it('renders visibility warning popover with `Learn more` link', () => {
const popover = findPopover();
expect(popover.exists()).toBe(true);
expect(
popover.findByRole('link', { name: GroupItem.i18n.learnMore }).attributes('href'),
).toBe(
helpPagePath('user/project/members/share_project_with_groups', {
anchor: 'sharing-projects-with-groups-of-a-higher-restrictive-visibility-level',
}),
);
});
} else {
itDoesNotRenderVisibilityWarningPopover();
@ -361,7 +374,7 @@ describe('GroupItemComponent', () => {
wrapper = createComponent({
group: {
...mockParentGroupItem,
visibility: VISIBILITY_PUBLIC,
visibility: VISIBILITY_LEVEL_PUBLIC,
type: ITEM_TYPE.PROJECT,
},
parentGroup: mockChildren[0],

View File

@ -6,7 +6,7 @@ import GroupItemComponent from '~/groups/components/group_item.vue';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import GroupsComponent from '~/groups/components/groups.vue';
import eventHub from '~/groups/event_hub';
import { VISIBILITY_PRIVATE } from '~/groups/constants';
import { VISIBILITY_LEVEL_PRIVATE } from '~/visibility_level/constants';
import { mockGroups, mockPageInfo } from '../mock_data';
describe('GroupsComponent', () => {
@ -26,7 +26,7 @@ describe('GroupsComponent', () => {
...propsData,
},
provide: {
currentGroupVisibility: VISIBILITY_PRIVATE,
currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE,
},
});
};

View File

@ -4,7 +4,10 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
let(:report) { instance_double('Gitlab::Ci::Reports::Sbom::Report') }
let(:report_data) { base_report_data }
let(:raw_report_data) { report_data.to_json }
let(:report_valid?) { true }
let(:validator_errors) { [] }
let(:base_report_data) do
{
@ -16,6 +19,13 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
subject(:parse!) { described_class.new(raw_report_data, report).parse! }
before do
allow_next_instance_of(Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator) do |validator|
allow(validator).to receive(:valid?).and_return(report_valid?)
allow(validator).to receive(:errors).and_return(validator_errors)
end
end
context 'when report JSON is invalid' do
let(:raw_report_data) { '{ ' }
@ -36,9 +46,19 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
end
end
context 'when cyclonedx report has no components' do
let(:report_data) { base_report_data }
context 'when report does not conform to the CycloneDX schema' do
let(:report_valid?) { false }
let(:validator_errors) { %w[error1 error2] }
it 'reports all errors returned by the validator' do
expect(report).to receive(:add_error).with("error1")
expect(report).to receive(:add_error).with("error2")
parse!
end
end
context 'when cyclonedx report has no components' do
it 'skips component processing' do
expect(report).not_to receive(:add_component)

View File

@ -0,0 +1,132 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator do
# Reports should be valid or invalid according to the specification at
# https://cyclonedx.org/docs/1.4/json/
subject(:validator) { described_class.new(report_data) }
let_it_be(:required_attributes) do
{
"bomFormat" => "CycloneDX",
"specVersion" => "1.4",
"version" => 1
}
end
context "with minimally valid report" do
let_it_be(:report_data) { required_attributes }
it { is_expected.to be_valid }
end
context "when report has components" do
let(:report_data) { required_attributes.merge({ "components" => components }) }
context "with minimally valid components" do
let(:components) do
[
{
"type" => "library",
"name" => "activesupport"
},
{
"type" => "library",
"name" => "byebug"
}
]
end
it { is_expected.to be_valid }
end
context "when components have versions" do
let(:components) do
[
{
"type" => "library",
"name" => "activesupport",
"version" => "5.1.4"
},
{
"type" => "library",
"name" => "byebug",
"version" => "10.0.0"
}
]
end
it { is_expected.to be_valid }
end
context "when components are not valid" do
let(:components) do
[
{ "type" => "foo" },
{ "name" => "activesupport" }
]
end
it { is_expected.not_to be_valid }
it "outputs errors for each validation failure" do
expect(validator.errors).to match_array([
"property '/components/0' is missing required keys: name",
"property '/components/0/type' is not one of: [\"application\", \"framework\"," \
" \"library\", \"container\", \"operating-system\", \"device\", \"firmware\", \"file\"]",
"property '/components/1' is missing required keys: type"
])
end
end
end
context "when report has metadata" do
let(:metadata) do
{
"timestamp" => "2022-02-23T08:02:39Z",
"tools" => [{ "vendor" => "GitLab", "name" => "Gemnasium", "version" => "2.34.0" }],
"authors" => [{ "name" => "GitLab", "email" => "support@gitlab.com" }]
}
end
let(:report_data) { required_attributes.merge({ "metadata" => metadata }) }
it { is_expected.to be_valid }
context "when metadata has properties" do
before do
metadata.merge!({ "properties" => properties })
end
context "when properties are valid" do
let(:properties) do
[
{ "name" => "gitlab:dependency_scanning:input_file", "value" => "Gemfile.lock" },
{ "name" => "gitlab:dependency_scanning:package_manager", "value" => "bundler" }
]
end
it { is_expected.to be_valid }
end
context "when properties are invalid" do
let(:properties) do
[
{ "name" => ["gitlab:meta:schema_version"], "value" => 1 }
]
end
it { is_expected.not_to be_valid }
it "outputs errors for each validation failure" do
expect(validator.errors).to match_array([
"property '/metadata/properties/0/name' is not of type: string",
"property '/metadata/properties/0/value' is not of type: string"
])
end
end
end
end
end

View File

@ -1,26 +1,19 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'test_prof/recipes/rspec/let_it_be'
require 'graphql'
require_relative '../../../../app/graphql/types/base_scalar'
require_relative '../../../../app/graphql/types/global_id_type'
require_relative '../../../support/helpers/global_id_deprecation_helpers'
TestProf::BeforeAll.adapter = Class.new do
def begin_transaction; end
def rollback_transaction; end
end.new
RSpec.describe Gitlab::GlobalId::Deprecations do
include GlobalIDDeprecationHelpers
let_it_be(:deprecation_1) do
let(:deprecation_1) do
described_class::NameDeprecation.new(old_name: 'Foo::Model', new_name: 'Bar', milestone: '9.0')
end
let_it_be(:deprecation_2) do
let(:deprecation_2) do
described_class::NameDeprecation.new(old_name: 'Baz', new_name: 'Qux::Model', milestone: '10.0')
end

View File

@ -442,7 +442,7 @@ RSpec.shared_examples 'prefixed token rotation' do
context 'token is not set' do
it 'generates a new token' do
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/)
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/o)
expect(instance).not_to be_persisted
end
end
@ -453,7 +453,7 @@ RSpec.shared_examples 'prefixed token rotation' do
end
it 'generates a new token' do
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/)
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/o)
expect(instance).not_to be_persisted
end
end
@ -475,7 +475,7 @@ RSpec.shared_examples 'prefixed token rotation' do
context 'token is not set' do
it 'generates a new token' do
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/)
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/o)
expect(instance).to be_persisted
end
end
@ -486,7 +486,7 @@ RSpec.shared_examples 'prefixed token rotation' do
end
it 'generates a new token' do
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/)
expect(subject).to match(/^#{RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX}/o)
expect(instance).to be_persisted
end
end

View File

@ -146,8 +146,8 @@ RSpec.describe Notes::CopyService do
new_note = to_noteable.notes.first
aggregate_failures do
expect(note.note).to match(/Simple text with image: #{FileUploader::MARKDOWN_PATTERN}/)
expect(new_note.note).to match(/Simple text with image: #{FileUploader::MARKDOWN_PATTERN}/)
expect(note.note).to match(/Simple text with image: #{FileUploader::MARKDOWN_PATTERN}/o)
expect(new_note.note).to match(/Simple text with image: #{FileUploader::MARKDOWN_PATTERN}/o)
expect(note.note).not_to eq(new_note.note)
expect(note.note_html).not_to eq(new_note.note_html)
end