Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7928b47c8e
commit
bf6d126a58
|
@ -2,6 +2,12 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 15.4.2 (2022-10-04)
|
||||
|
||||
### Fixed (1 change)
|
||||
|
||||
- [Ensure that stage name and record are in sync for page deployments](gitlab-org/gitlab@ce58ec2ef959bd35ddbc6992560a163c8fc4f145) ([merge request](gitlab-org/gitlab!100037))
|
||||
|
||||
## 15.4.1 (2022-09-29)
|
||||
|
||||
### Security (15 changes)
|
||||
|
|
|
@ -1 +1 @@
|
|||
e02759b6d805b0eadfeba21a67bec6de75f9a750
|
||||
5c49f857a37ee47158e2caff7c963e7de9563e8b
|
||||
|
|
|
@ -280,6 +280,7 @@ export function renderTableRow(state, node) {
|
|||
}
|
||||
|
||||
export function renderTable(state, node) {
|
||||
state.flushClose();
|
||||
setIsInBlockTable(node, shouldRenderHTMLTable(node));
|
||||
|
||||
if (isInBlockTable(node)) renderTagOpen(state, 'table');
|
||||
|
|
|
@ -981,20 +981,20 @@ export default {
|
|||
name="project[project_feature_attributes][feature_flags_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
<project-setting-row
|
||||
ref="releases-settings"
|
||||
:label="$options.i18n.releasesLabel"
|
||||
:help-text="$options.i18n.releasesHelpText"
|
||||
:help-path="releasesHelpPath"
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="releasesAccessLevel"
|
||||
:label="$options.i18n.releasesLabel"
|
||||
:options="featureAccessLevelOptions"
|
||||
name="project[project_feature_attributes][releases_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
</template>
|
||||
<project-setting-row
|
||||
ref="releases-settings"
|
||||
:label="$options.i18n.releasesLabel"
|
||||
:help-text="$options.i18n.releasesHelpText"
|
||||
:help-path="releasesHelpPath"
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="releasesAccessLevel"
|
||||
:label="$options.i18n.releasesLabel"
|
||||
:options="featureAccessLevelOptions"
|
||||
name="project[project_feature_attributes][releases_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
</div>
|
||||
<project-setting-row v-if="canDisableEmails" ref="email-settings" class="mb-3">
|
||||
<label class="js-emails-disabled">
|
||||
|
|
|
@ -21,7 +21,9 @@ export const i18n = {
|
|||
),
|
||||
description: s__(
|
||||
`SecurityConfiguration|Once you've enabled a scan for the default branch,
|
||||
any subsequent feature branch you create will include the scan.`,
|
||||
any subsequent feature branch you create will include the scan. An enabled
|
||||
scanner will not be reflected as such until the pipeline has been
|
||||
successfully executed and it has generated valid artifacts.`,
|
||||
),
|
||||
securityConfiguration: __('Security Configuration'),
|
||||
vulnerabilityManagement: s__('SecurityConfiguration|Vulnerability Management'),
|
||||
|
|
|
@ -22,7 +22,7 @@ export default {
|
|||
s__('SecurityConfiguration|High-level vulnerability statistics across projects and groups'),
|
||||
s__('SecurityConfiguration|Runtime security metrics for application environments'),
|
||||
s__(
|
||||
'SecurityConfiguration|More scan types, including Container Scanning, DAST, Dependency Scanning, Fuzzing, and Licence Compliance',
|
||||
'SecurityConfiguration|More scan types, including DAST, Dependency Scanning, Fuzzing, and Licence Compliance',
|
||||
),
|
||||
],
|
||||
buttonText: s__('SecurityConfiguration|Upgrade or start a free trial'),
|
||||
|
|
|
@ -435,14 +435,14 @@ class ProjectsController < Projects::ApplicationController
|
|||
analytics_access_level
|
||||
security_and_compliance_access_level
|
||||
container_registry_access_level
|
||||
releases_access_level
|
||||
] + operations_feature_attributes
|
||||
end
|
||||
|
||||
def operations_feature_attributes
|
||||
if Feature.enabled?(:split_operations_visibility_permissions, project)
|
||||
%i[
|
||||
environments_access_level feature_flags_access_level releases_access_level
|
||||
monitor_access_level
|
||||
environments_access_level feature_flags_access_level monitor_access_level
|
||||
]
|
||||
else
|
||||
%i[operations_access_level]
|
||||
|
|
|
@ -10,4 +10,24 @@ class BulkImports::Failure < ApplicationRecord
|
|||
optional: false
|
||||
|
||||
validates :entity, presence: true
|
||||
|
||||
def relation
|
||||
pipeline_relation || default_relation
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pipeline_relation
|
||||
klass = pipeline_class.constantize
|
||||
|
||||
return unless klass.ancestors.include?(BulkImports::Pipeline)
|
||||
|
||||
klass.relation
|
||||
rescue NameError
|
||||
nil
|
||||
end
|
||||
|
||||
def default_relation
|
||||
pipeline_class.demodulize.chomp('Pipeline').underscore
|
||||
end
|
||||
end
|
||||
|
|
|
@ -227,7 +227,7 @@ module Ci
|
|||
yaml_variables when environment coverage_regex
|
||||
description tag_list protected needs_attributes
|
||||
job_variables_attributes resource_group scheduling_type
|
||||
ci_stage partition_id].freeze
|
||||
ci_stage partition_id id_tokens].freeze
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1204,6 +1204,14 @@ module Ci
|
|||
end
|
||||
|
||||
def job_jwt_variables
|
||||
if project.ci_cd_settings.opt_in_jwt?
|
||||
id_tokens_variables
|
||||
else
|
||||
legacy_jwt_variables.concat(id_tokens_variables)
|
||||
end
|
||||
end
|
||||
|
||||
def legacy_jwt_variables
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
break variables unless Feature.enabled?(:ci_job_jwt, project)
|
||||
|
||||
|
@ -1217,6 +1225,20 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def id_tokens_variables
|
||||
return [] unless id_tokens?
|
||||
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
id_tokens.each do |var_name, token_data|
|
||||
token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['id_token']['aud'])
|
||||
|
||||
variables.append(key: var_name, value: token, public: false, masked: true)
|
||||
end
|
||||
rescue OpenSSL::PKey::RSAError, Gitlab::Ci::Jwt::NoSigningKeyError => e
|
||||
Gitlab::ErrorTracking.track_exception(e)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_for_online_runners(&block)
|
||||
Rails.cache.fetch(
|
||||
['has-online-runners', id],
|
||||
|
|
|
@ -80,7 +80,7 @@ module Ci
|
|||
end
|
||||
|
||||
def id_tokens?
|
||||
!!metadata&.id_tokens?
|
||||
metadata&.id_tokens.present?
|
||||
end
|
||||
|
||||
def id_tokens=(value)
|
||||
|
|
|
@ -21,7 +21,7 @@ module Integrations
|
|||
end
|
||||
|
||||
def help
|
||||
s_("HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use.")
|
||||
s_("HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use.")
|
||||
end
|
||||
|
||||
def hostname
|
||||
|
|
|
@ -789,8 +789,8 @@ class Repository
|
|||
Commit.order_by(collection: commits, order_by: order_by, sort: sort)
|
||||
end
|
||||
|
||||
def branch_names_contains(sha)
|
||||
raw_repository.branch_names_contains_sha(sha)
|
||||
def branch_names_contains(sha, limit: 0)
|
||||
raw_repository.branch_names_contains_sha(sha, limit: limit)
|
||||
end
|
||||
|
||||
def tag_names_contains(sha, limit: 0)
|
||||
|
|
|
@ -399,7 +399,7 @@ class ProjectPolicy < BasePolicy
|
|||
prevent(:admin_feature_flags_client)
|
||||
end
|
||||
|
||||
rule { split_operations_visibility_permissions & releases_disabled }.policy do
|
||||
rule { releases_disabled }.policy do
|
||||
prevent(*create_read_update_admin_destroy(:release))
|
||||
end
|
||||
|
||||
|
|
|
@ -3,17 +3,43 @@ module Packages
|
|||
module Rpm
|
||||
module RepositoryMetadata
|
||||
class BaseBuilder
|
||||
def initialize(xml: nil, data: {})
|
||||
@xml = Nokogiri::XML(xml) if xml.present?
|
||||
@data = data
|
||||
end
|
||||
|
||||
def execute
|
||||
build_empty_structure
|
||||
return build_empty_structure if xml.blank?
|
||||
|
||||
update_xml_document
|
||||
update_package_count
|
||||
xml.to_xml
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :xml, :data
|
||||
|
||||
def build_empty_structure
|
||||
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
||||
xml.method_missing(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES)
|
||||
end.to_xml
|
||||
end
|
||||
|
||||
def update_xml_document
|
||||
# Add to the root xml element a new package metadata node
|
||||
xml.at(self.class::ROOT_TAG).add_child(build_new_node)
|
||||
end
|
||||
|
||||
def update_package_count
|
||||
packages_count = xml.css("//#{self.class::ROOT_TAG}/package").count
|
||||
|
||||
xml.at(self.class::ROOT_TAG).attributes["packages"].value = packages_count.to_s
|
||||
end
|
||||
|
||||
def build_new_node
|
||||
raise NotImplementedError, "#{self.class} should implement #{__method__}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,79 @@ module Packages
|
|||
'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm',
|
||||
packages: '0'
|
||||
}.freeze
|
||||
|
||||
# Nodes that have only text without attributes
|
||||
REQUIRED_BASE_ATTRIBUTES = %i[name arch summary description].freeze
|
||||
NOT_REQUIRED_BASE_ATTRIBUTES = %i[url packager].freeze
|
||||
FORMAT_NODE_BASE_ATTRIBUTES = %i[license vendor group buildhost sourcerpm].freeze
|
||||
|
||||
private
|
||||
|
||||
def build_new_node
|
||||
builder = Nokogiri::XML::Builder.new do |xml|
|
||||
xml.package(type: :rpm, 'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm') do
|
||||
build_required_base_attributes(xml)
|
||||
build_not_required_base_attributes(xml)
|
||||
xml.version epoch: data[:epoch], ver: data[:version], rel: data[:release]
|
||||
xml.checksum data[:checksum], type: 'sha256', pkgid: 'YES'
|
||||
xml.size package: data[:packagesize], installed: data[:installedsize], archive: data[:archivesize]
|
||||
xml.time file: data[:filetime], build: data[:buildtime]
|
||||
xml.location href: data[:location] if data[:location].present?
|
||||
build_format_node(xml)
|
||||
end
|
||||
end
|
||||
|
||||
Nokogiri::XML(builder.to_xml).at('package')
|
||||
end
|
||||
|
||||
def build_required_base_attributes(xml)
|
||||
REQUIRED_BASE_ATTRIBUTES.each do |attribute|
|
||||
xml.method_missing(attribute, data[attribute])
|
||||
end
|
||||
end
|
||||
|
||||
def build_not_required_base_attributes(xml)
|
||||
NOT_REQUIRED_BASE_ATTRIBUTES.each do |attribute|
|
||||
xml.method_missing(attribute, data[attribute]) if data[attribute].present?
|
||||
end
|
||||
end
|
||||
|
||||
def build_format_node(xml)
|
||||
xml.format do
|
||||
build_base_format_attributes(xml)
|
||||
build_provides_node(xml)
|
||||
build_requires_node(xml)
|
||||
end
|
||||
end
|
||||
|
||||
def build_base_format_attributes(xml)
|
||||
FORMAT_NODE_BASE_ATTRIBUTES.each do |attribute|
|
||||
xml[:rpm].method_missing(attribute, data[attribute]) if data[attribute].present?
|
||||
end
|
||||
end
|
||||
|
||||
def build_requires_node(xml)
|
||||
xml[:rpm].requires do
|
||||
data[:requirements].each do |requires|
|
||||
xml[:rpm].entry(
|
||||
name: requires[:requirename],
|
||||
flags: requires[:requireflags],
|
||||
ver: requires[:requireversion]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_provides_node(xml)
|
||||
xml[:rpm].provides do
|
||||
data[:provides].each do |provides|
|
||||
xml[:rpm].entry(
|
||||
name: provides[:providename],
|
||||
flags: provides[:provideflags],
|
||||
ver: provides[:provideversion])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"^file$": { "type": "boolean" }
|
||||
"^file$": { "type": "boolean" },
|
||||
"^token$": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ module BulkImports
|
|||
return skip_tracker if pipeline_tracker.entity.failed?
|
||||
|
||||
raise(Pipeline::ExpiredError, 'Pipeline timeout') if job_timeout?
|
||||
raise(Pipeline::FailedError, export_status.error) if export_failed?
|
||||
raise(Pipeline::FailedError, "Export from source instance failed: #{export_status.error}") if export_failed?
|
||||
|
||||
return re_enqueue if export_empty? || export_started?
|
||||
|
||||
|
|
|
@ -37,7 +37,9 @@ module Gitlab
|
|||
|
||||
importer_class.new(object, project, client).execute
|
||||
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
|
||||
if increment_object_counter?(object)
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
|
||||
end
|
||||
|
||||
info(project.id, message: 'importer finished')
|
||||
rescue NoMethodError => e
|
||||
|
@ -49,6 +51,10 @@ module Gitlab
|
|||
track_and_raise_exception(project, e)
|
||||
end
|
||||
|
||||
def increment_object_counter?(_object)
|
||||
true
|
||||
end
|
||||
|
||||
def object_type
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
|
|
@ -16,6 +16,10 @@ module Gitlab
|
|||
def object_type
|
||||
:issue
|
||||
end
|
||||
|
||||
def increment_object_counter?(object)
|
||||
!object.pull_request?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -150,11 +150,14 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
|
|||
"updated_at": "2021-06-24T10:40:46.590Z",
|
||||
"failures": [
|
||||
{
|
||||
"pipeline_class": "BulkImports::Groups::Pipelines::GroupPipeline",
|
||||
"pipeline_step": "extractor",
|
||||
"relation": "group",
|
||||
"step": "extractor",
|
||||
"exception_message": "Error!",
|
||||
"exception_class": "Exception",
|
||||
"correlation_id_value": "dfcf583058ed4508e4c7c617bd7f0edd",
|
||||
"created_at": "2021-06-24T10:40:46.495Z"
|
||||
"created_at": "2021-06-24T10:40:46.495Z",
|
||||
"pipeline_class": "BulkImports::Groups::Pipelines::GroupPipeline",
|
||||
"pipeline_step": "extractor"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1229,6 +1229,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your-token>" \
|
|||
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
|
||||
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
|
||||
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
|
||||
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
|
||||
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrator only)_ |
|
||||
|
@ -1307,6 +1308,7 @@ POST /projects/user/:user_id
|
|||
| `path` | string | **{dotted-circle}** No | Custom repository name for new project. By default generated based on name. |
|
||||
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
|
||||
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
|
||||
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
|
||||
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
|
||||
|
@ -1408,6 +1410,7 @@ Supported attributes:
|
|||
| `path` | string | **{dotted-circle}** No | Custom repository name for the project. By default generated based on name. |
|
||||
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
|
||||
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
|
||||
| `releases_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
|
||||
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
|
||||
| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
|
||||
|
|
|
@ -92,7 +92,8 @@ Use the toggles to enable or disable features in the project.
|
|||
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md). |
|
||||
| **Pages** | ✓ | Allows you to [publish static websites](../pages/index.md). |
|
||||
| **Operations** | ✓ | Control access to Operations-related features, including [Operations Dashboard](../../../operations/index.md), [Environments and Deployments](../../../ci/environments/index.md), [Feature Flags](../../../operations/feature_flags.md). |
|
||||
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
|
||||
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
|
||||
| **Releases** | ✓ | Control access to [Releases](../releases/index.md). |
|
||||
|
||||
When you disable a feature, the following additional features are also disabled:
|
||||
|
||||
|
|
|
@ -4,14 +4,16 @@ module API
|
|||
module Entities
|
||||
module BulkImports
|
||||
class EntityFailure < Grape::Entity
|
||||
expose :relation
|
||||
expose :pipeline_step, as: :step
|
||||
expose :exception_message do |failure|
|
||||
::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
|
||||
end
|
||||
expose :exception_class
|
||||
expose :pipeline_class
|
||||
expose :pipeline_step
|
||||
expose :correlation_id_value
|
||||
expose :created_at
|
||||
expose :pipeline_class
|
||||
expose :pipeline_step
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,6 +80,7 @@ module API
|
|||
expose(:analytics_access_level) { |project, options| project_feature_string_access_level(project, :analytics) }
|
||||
expose(:container_registry_access_level) { |project, options| project_feature_string_access_level(project, :container_registry) }
|
||||
expose(:security_and_compliance_access_level) { |project, options| project_feature_string_access_level(project, :security_and_compliance) }
|
||||
expose(:releases_access_level) { |project, options| project_feature_string_access_level(project, :releases) }
|
||||
|
||||
expose :emails_disabled
|
||||
expose :shared_runners_enabled
|
||||
|
|
|
@ -40,6 +40,8 @@ module API
|
|||
end
|
||||
|
||||
put 'authorize' do
|
||||
project = authorized_user_project
|
||||
|
||||
authorize_workhorse!(subject: project, maximum_size: project.actual_limits.generic_packages_max_file_size)
|
||||
end
|
||||
|
||||
|
@ -59,6 +61,8 @@ module API
|
|||
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
|
||||
|
||||
put do
|
||||
project = authorized_user_project
|
||||
|
||||
authorize_upload!(project)
|
||||
bad_request!('File is too large') if max_file_size_exceeded?
|
||||
|
||||
|
@ -95,6 +99,8 @@ module API
|
|||
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
|
||||
|
||||
get do
|
||||
project = authorized_user_project(action: :read_package)
|
||||
|
||||
authorize_read_package!(project)
|
||||
|
||||
package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version])
|
||||
|
@ -112,12 +118,8 @@ module API
|
|||
include ::API::Helpers::PackagesHelpers
|
||||
include ::API::Helpers::Packages::BasicAuthHelpers
|
||||
|
||||
def project
|
||||
authorized_user_project
|
||||
end
|
||||
|
||||
def max_file_size_exceeded?
|
||||
project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
|
||||
authorized_user_project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,15 +14,27 @@ module API
|
|||
include Constants
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def authorized_user_project
|
||||
@authorized_user_project ||= authorized_project_find!
|
||||
def authorized_user_project(action: :read_project)
|
||||
strong_memoize("authorized_user_project_#{action}") do
|
||||
authorized_project_find!(action: action)
|
||||
end
|
||||
end
|
||||
|
||||
def authorized_project_find!
|
||||
def authorized_project_find!(action: :read_project)
|
||||
project = find_project(params[:id])
|
||||
|
||||
unless project && can?(current_user, :read_project, project)
|
||||
return unauthorized_or! { not_found! }
|
||||
return unauthorized_or! { not_found! } unless project
|
||||
|
||||
case action
|
||||
when :read_package
|
||||
unless can?(current_user, :read_package, project&.packages_policy_subject)
|
||||
# guest users can have :read_project but not :read_package
|
||||
return forbidden! if can?(current_user, :read_project, project)
|
||||
|
||||
return unauthorized_or! { not_found! }
|
||||
end
|
||||
else
|
||||
return unauthorized_or! { not_found! } unless can?(current_user, action, project)
|
||||
end
|
||||
|
||||
project
|
||||
|
|
|
@ -36,6 +36,7 @@ module API
|
|||
optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
|
||||
optional :container_registry_access_level, type: String, values: %w(disabled private enabled), desc: 'Controls visibility of the container registry. One of `disabled`, `private` or `enabled`. `private` will make the container registry accessible only to project members (reporter role and above). `enabled` will make the container registry accessible to everyone who has access to the project. `disabled` will disable the container registry'
|
||||
optional :security_and_compliance_access_level, type: String, values: %w(disabled private enabled), desc: 'Security and compliance access level. One of `disabled`, `private` or `enabled`'
|
||||
optional :releases_access_level, type: String, values: %w(disabled private enabled), desc: 'Releases access level. One of `disabled`, `private` or `enabled`'
|
||||
|
||||
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
|
||||
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
|
||||
|
@ -179,6 +180,7 @@ module API
|
|||
:keep_latest_artifact,
|
||||
:mr_default_target_self,
|
||||
:enforce_auth_checks_on_uploads,
|
||||
:releases_access_level,
|
||||
|
||||
# TODO: remove in API v5, replaced by *_access_level
|
||||
:issues_enabled,
|
||||
|
|
|
@ -183,7 +183,11 @@ module BulkImports
|
|||
end
|
||||
|
||||
def relation
|
||||
class_attributes[:relation_name]
|
||||
class_attributes[:relation_name] || default_relation
|
||||
end
|
||||
|
||||
def default_relation
|
||||
self.name.demodulize.chomp('Pipeline').underscore
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
self.new(build, ttl: build.metadata_timeout).encoded
|
||||
end
|
||||
|
||||
def initialize(build, ttl: nil)
|
||||
def initialize(build, ttl:)
|
||||
@build = build
|
||||
@ttl = ttl
|
||||
end
|
||||
|
|
|
@ -3,13 +3,27 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class JwtV2 < Jwt
|
||||
DEFAULT_AUD = Settings.gitlab.base_url
|
||||
|
||||
def self.for_build(build, aud: DEFAULT_AUD)
|
||||
new(build, ttl: build.metadata_timeout, aud: aud).encoded
|
||||
end
|
||||
|
||||
def initialize(build, ttl:, aud:)
|
||||
super(build, ttl: ttl)
|
||||
|
||||
@aud = aud
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :aud
|
||||
|
||||
def reserved_claims
|
||||
super.merge(
|
||||
iss: Settings.gitlab.base_url,
|
||||
sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
|
||||
aud: Settings.gitlab.base_url
|
||||
aud: aud
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1027,8 +1027,8 @@ module Gitlab
|
|||
@praefect_info_client ||= Gitlab::GitalyClient::PraefectInfoService.new(self)
|
||||
end
|
||||
|
||||
def branch_names_contains_sha(sha)
|
||||
gitaly_ref_client.branch_names_contains_sha(sha)
|
||||
def branch_names_contains_sha(sha, limit: 0)
|
||||
gitaly_ref_client.branch_names_contains_sha(sha, limit: limit)
|
||||
end
|
||||
|
||||
def tag_names_contains_sha(sha, limit: 0)
|
||||
|
|
|
@ -40,6 +40,10 @@ module Gitlab
|
|||
{ state: 'all', sort: 'created', direction: 'asc' }
|
||||
end
|
||||
|
||||
def increment_object_counter?(object)
|
||||
object[:pull_request].nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def additional_object_data
|
||||
|
|
|
@ -129,7 +129,9 @@ module Gitlab
|
|||
|
||||
next if already_imported?(object)
|
||||
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
|
||||
if increment_object_counter?(object)
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
|
||||
end
|
||||
|
||||
yield object
|
||||
|
||||
|
@ -140,6 +142,10 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def increment_object_counter?(_object)
|
||||
true
|
||||
end
|
||||
|
||||
# Returns true if the given object has already been imported, false
|
||||
# otherwise.
|
||||
#
|
||||
|
|
|
@ -14,11 +14,13 @@ module Gitlab
|
|||
PROC_SMAPS_ROLLUP_PATH = '/proc/%s/smaps_rollup'
|
||||
PROC_LIMITS_PATH = '/proc/self/limits'
|
||||
PROC_FD_GLOB = '/proc/self/fd/*'
|
||||
PROC_MEM_INFO = '/proc/meminfo'
|
||||
|
||||
PRIVATE_PAGES_PATTERN = /^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/.freeze
|
||||
PSS_PATTERN = /^Pss:\s+(?<value>\d+)/.freeze
|
||||
RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze
|
||||
MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze
|
||||
MEM_TOTAL_PATTERN = /^MemTotal:\s+(?<value>\d+) (.+)/.freeze
|
||||
|
||||
def summary
|
||||
proportional_mem = memory_usage_uss_pss
|
||||
|
@ -45,6 +47,10 @@ module Gitlab
|
|||
.transform_values(&:kilobytes)
|
||||
end
|
||||
|
||||
def memory_total
|
||||
sum_matches(PROC_MEM_INFO, memory_total: MEM_TOTAL_PATTERN)[:memory_total].kilobytes
|
||||
end
|
||||
|
||||
def file_descriptor_count
|
||||
Dir.glob(PROC_FD_GLOB).length
|
||||
end
|
||||
|
|
|
@ -51,9 +51,10 @@ module Gitlab
|
|||
|
||||
def refresh_state(phase)
|
||||
@phase = PHASE.fetch(phase)
|
||||
@current_rss = get_rss
|
||||
@soft_limit_rss = get_soft_limit_rss
|
||||
@hard_limit_rss = get_hard_limit_rss
|
||||
@current_rss = get_rss_kb
|
||||
@soft_limit_rss = get_soft_limit_rss_kb
|
||||
@hard_limit_rss = get_hard_limit_rss_kb
|
||||
@memory_total = get_memory_total_kb
|
||||
|
||||
# track the current state as prometheus gauges
|
||||
@metrics[:sidekiq_memory_killer_phase].set({}, @phase)
|
||||
|
@ -178,6 +179,7 @@ module Gitlab
|
|||
current_rss: @current_rss,
|
||||
soft_limit_rss: @soft_limit_rss,
|
||||
hard_limit_rss: @hard_limit_rss,
|
||||
memory_total_kb: @memory_total,
|
||||
reason: reason,
|
||||
running_jobs: running_jobs)
|
||||
|
||||
|
@ -214,18 +216,19 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def get_rss
|
||||
output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s)
|
||||
return 0 unless status&.zero?
|
||||
|
||||
output.to_i
|
||||
def get_memory_total_kb
|
||||
Gitlab::Metrics::System.memory_total / 1.kilobytes
|
||||
end
|
||||
|
||||
def get_soft_limit_rss
|
||||
def get_rss_kb
|
||||
Gitlab::Metrics::System.memory_usage_rss / 1.kilobytes
|
||||
end
|
||||
|
||||
def get_soft_limit_rss_kb
|
||||
SOFT_LIMIT_RSS_KB + rss_increase_by_jobs
|
||||
end
|
||||
|
||||
def get_hard_limit_rss
|
||||
def get_hard_limit_rss_kb
|
||||
HARD_LIMIT_RSS_KB
|
||||
end
|
||||
|
||||
|
|
|
@ -19473,7 +19473,7 @@ msgstr ""
|
|||
msgid "Harbor Registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use."
|
||||
msgid "HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use."
|
||||
msgstr ""
|
||||
|
||||
msgid "HarborIntegration|Base URL of the Harbor instance."
|
||||
|
@ -35649,13 +35649,13 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Manage profiles for use by DAST scans."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|More scan types, including Container Scanning, DAST, Dependency Scanning, Fuzzing, and Licence Compliance"
|
||||
msgid "SecurityConfiguration|More scan types, including DAST, Dependency Scanning, Fuzzing, and Licence Compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Not enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
|
||||
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan. An enabled scanner will not be reflected as such until the pipeline has been successfully executed and it has generated valid artifacts."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Quickly enable all continuous testing and compliance tools by enabling %{linkStart}Auto DevOps%{linkEnd}"
|
||||
|
@ -36291,7 +36291,7 @@ msgstr ""
|
|||
msgid "SecurityReports|Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Still Detected"
|
||||
msgid "SecurityReports|Still detected"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|Submit vulnerability"
|
||||
|
|
|
@ -54,7 +54,7 @@ module QA
|
|||
def verify_status_data
|
||||
stats = imported_project.project_import_status.dig(:stats, :imported)
|
||||
expect(stats).to include(
|
||||
# issue: 2,
|
||||
issue: 1,
|
||||
label: 9,
|
||||
milestone: 1,
|
||||
note: 3,
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Repository License Detection', product_group: :source_code do
|
||||
describe 'Repository License Detection', product_group: :source_code, quarantine: {
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/376154',
|
||||
type: :stale
|
||||
} do
|
||||
after do
|
||||
project.remove_via_api!
|
||||
end
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"files": [
|
||||
"/usr/bin/hello.sh"
|
||||
],
|
||||
"changelogs": [
|
||||
{
|
||||
"changelogtext": "First build",
|
||||
"changelogtime": 1662552000
|
||||
}
|
||||
],
|
||||
"requirements": [
|
||||
{
|
||||
"requirename": "bash",
|
||||
"requireversion": "",
|
||||
"requireflags": 0
|
||||
},
|
||||
{
|
||||
"requirename": "rpmlib(CompressedFileNames)",
|
||||
"requireversion": "3.0.4-1",
|
||||
"requireflags": 16777226
|
||||
}
|
||||
],
|
||||
"provides": [
|
||||
{
|
||||
"providename": "hello",
|
||||
"provideflags": 8,
|
||||
"provideversion": "0.0.1-1.fc29"
|
||||
},
|
||||
{
|
||||
"providename": "hello(x86-64)",
|
||||
"provideflags": 8,
|
||||
"provideversion": "0.0.1-1.fc29"
|
||||
}
|
||||
],
|
||||
"name": "hello",
|
||||
"version": "0.0.1",
|
||||
"release": "1.fc29",
|
||||
"summary": "Simple RPM package",
|
||||
"description": "Fake rpm package",
|
||||
"arch": "x86_64",
|
||||
"license": "MIT",
|
||||
"sourcerpm": "hello-0.0.1-1.fc29.src.rpm",
|
||||
"group": "Unspecified",
|
||||
"buildhost": "localhost",
|
||||
"packager": null,
|
||||
"vendor": null
|
||||
}
|
|
@ -1,40 +1,36 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import { nextTick } from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import DraftsCount from '~/batch_comments/components/drafts_count.vue';
|
||||
import { createStore } from '~/batch_comments/stores';
|
||||
|
||||
describe('Batch comments drafts count component', () => {
|
||||
let vm;
|
||||
let Component;
|
||||
|
||||
beforeAll(() => {
|
||||
Component = Vue.extend(DraftsCount);
|
||||
});
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
const store = createStore();
|
||||
store = createStore();
|
||||
|
||||
store.state.batchComments.drafts.push('comment');
|
||||
|
||||
vm = mountComponentWithStore(Component, { store });
|
||||
wrapper = mount(DraftsCount, { store });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders count', () => {
|
||||
expect(vm.$el.textContent).toContain('1');
|
||||
expect(wrapper.text()).toContain('1');
|
||||
});
|
||||
|
||||
it('renders screen reader text', async () => {
|
||||
const el = vm.$el.querySelector('.sr-only');
|
||||
const el = wrapper.find('.sr-only');
|
||||
|
||||
expect(el.textContent).toContain('draft');
|
||||
|
||||
vm.$store.state.batchComments.drafts.push('comment 2');
|
||||
expect(el.text()).toContain('draft');
|
||||
|
||||
store.state.batchComments.drafts.push('comment 2');
|
||||
await nextTick();
|
||||
expect(el.textContent).toContain('drafts');
|
||||
|
||||
expect(el.text()).toContain('drafts');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Vue from 'vue';
|
||||
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import PreviewItem from '~/batch_comments/components/preview_item.vue';
|
||||
import { createStore } from '~/batch_comments/stores';
|
||||
import diffsModule from '~/diffs/store/modules';
|
||||
|
@ -8,8 +7,7 @@ import '~/behaviors/markdown/render_gfm';
|
|||
import { createDraft } from '../mock_data';
|
||||
|
||||
describe('Batch comments draft preview item component', () => {
|
||||
let vm;
|
||||
let Component;
|
||||
let wrapper;
|
||||
let draft;
|
||||
|
||||
function createComponent(isLast = false, extra = {}, extendStore = () => {}) {
|
||||
|
@ -24,21 +22,17 @@ describe('Batch comments draft preview item component', () => {
|
|||
...extra,
|
||||
};
|
||||
|
||||
vm = mountComponentWithStore(Component, { store, props: { draft, isLast } });
|
||||
wrapper = mount(PreviewItem, { store, propsData: { draft, isLast } });
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
Component = Vue.extend(PreviewItem);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders text content', () => {
|
||||
createComponent(false, { note_html: '<img src="" /><p>Hello world</p>' });
|
||||
|
||||
expect(vm.$el.querySelector('.review-preview-item-content').innerHTML).toEqual(
|
||||
expect(wrapper.find('.review-preview-item-content').element.innerHTML).toBe(
|
||||
'<p>Hello world</p>',
|
||||
);
|
||||
});
|
||||
|
@ -47,9 +41,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
it('renders file path', () => {
|
||||
createComponent(false, { file_path: 'index.js', file_hash: 'abc', position: {} });
|
||||
|
||||
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
|
||||
'index.js',
|
||||
);
|
||||
expect(wrapper.find('.review-preview-item-header-text').text()).toContain('index.js');
|
||||
});
|
||||
|
||||
it('renders new line position', () => {
|
||||
|
@ -66,7 +58,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.bold').textContent).toContain(':+1');
|
||||
expect(wrapper.find('.bold').text()).toContain(':+1');
|
||||
});
|
||||
|
||||
it('renders old line position', () => {
|
||||
|
@ -82,7 +74,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.bold').textContent).toContain(':2');
|
||||
expect(wrapper.find('.bold').text()).toContain(':2');
|
||||
});
|
||||
|
||||
it('renders image position', () => {
|
||||
|
@ -92,7 +84,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
position: { position_type: 'image', x: 10, y: 20 },
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.bold').textContent).toContain('10x 20y');
|
||||
expect(wrapper.find('.bold').text()).toContain('10x 20y');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -113,15 +105,13 @@ describe('Batch comments draft preview item component', () => {
|
|||
});
|
||||
|
||||
it('renders title', () => {
|
||||
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
|
||||
expect(wrapper.find('.review-preview-item-header-text').text()).toContain(
|
||||
"Author 'Nick' Name's thread",
|
||||
);
|
||||
});
|
||||
|
||||
it('renders thread resolved text', () => {
|
||||
expect(vm.$el.querySelector('.draft-note-resolution').textContent).toContain(
|
||||
'Thread will be resolved',
|
||||
);
|
||||
expect(wrapper.find('.draft-note-resolution').text()).toContain('Thread will be resolved');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -131,9 +121,7 @@ describe('Batch comments draft preview item component', () => {
|
|||
store.state.notes.discussions.push({});
|
||||
});
|
||||
|
||||
expect(vm.$el.querySelector('.review-preview-item-header-text').textContent).toContain(
|
||||
'Your new comment',
|
||||
);
|
||||
expect(wrapper.find('.review-preview-item-header-text').text()).toContain('Your new comment');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,38 +1,34 @@
|
|||
import Vue, { nextTick } from 'vue';
|
||||
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import { nextTick } from 'vue';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import PublishButton from '~/batch_comments/components/publish_button.vue';
|
||||
import { createStore } from '~/batch_comments/stores';
|
||||
|
||||
describe('Batch comments publish button component', () => {
|
||||
let vm;
|
||||
let Component;
|
||||
|
||||
beforeAll(() => {
|
||||
Component = Vue.extend(PublishButton);
|
||||
});
|
||||
let wrapper;
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
const store = createStore();
|
||||
store = createStore();
|
||||
|
||||
vm = mountComponentWithStore(Component, { store, props: { shouldPublish: true } });
|
||||
wrapper = mount(PublishButton, { store, propsData: { shouldPublish: true } });
|
||||
|
||||
jest.spyOn(vm.$store, 'dispatch').mockImplementation();
|
||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('dispatches publishReview on click', () => {
|
||||
vm.$el.click();
|
||||
it('dispatches publishReview on click', async () => {
|
||||
await wrapper.trigger('click');
|
||||
|
||||
expect(vm.$store.dispatch).toHaveBeenCalledWith('batchComments/publishReview', undefined);
|
||||
expect(store.dispatch).toHaveBeenCalledWith('batchComments/publishReview', undefined);
|
||||
});
|
||||
|
||||
it('sets loading when isPublishing is true', async () => {
|
||||
vm.$store.state.batchComments.isPublishing = true;
|
||||
store.state.batchComments.isPublishing = true;
|
||||
|
||||
await nextTick();
|
||||
expect(vm.$el.getAttribute('disabled')).toBe('disabled');
|
||||
expect(wrapper.attributes('disabled')).toBe('disabled');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1204,6 +1204,24 @@ Oranges are orange [^1]
|
|||
);
|
||||
});
|
||||
|
||||
it('correctly adds a space between a preceding block element and a markdown table', () => {
|
||||
expect(
|
||||
serialize(
|
||||
bulletList(listItem(paragraph('List item 1')), listItem(paragraph('List item 2'))),
|
||||
table(tableRow(tableHeader(paragraph('header'))), tableRow(tableCell(paragraph('cell')))),
|
||||
).trim(),
|
||||
).toBe(
|
||||
`
|
||||
* List item 1
|
||||
* List item 2
|
||||
|
||||
| header |
|
||||
|--------|
|
||||
| cell |
|
||||
`.trim(),
|
||||
);
|
||||
});
|
||||
|
||||
it('correctly serializes reference definition', () => {
|
||||
expect(
|
||||
serialize(
|
||||
|
|
|
@ -79,7 +79,7 @@ describe('UpgradeBanner component', () => {
|
|||
expect(wrapperText).toContain('statistics in the merge request');
|
||||
expect(wrapperText).toContain('statistics across projects');
|
||||
expect(wrapperText).toContain('Runtime security metrics');
|
||||
expect(wrapperText).toContain('More scan types, including Container Scanning,');
|
||||
expect(wrapperText).toContain('More scan types, including DAST,');
|
||||
});
|
||||
|
||||
describe('when user interacts', () => {
|
||||
|
|
|
@ -9,8 +9,8 @@ RSpec.describe API::Entities::BulkImports::EntityFailure do
|
|||
|
||||
it 'has the correct attributes' do
|
||||
expect(subject).to include(
|
||||
:pipeline_class,
|
||||
:pipeline_step,
|
||||
:relation,
|
||||
:step,
|
||||
:exception_class,
|
||||
:exception_message,
|
||||
:correlation_id_value,
|
||||
|
|
|
@ -20,16 +20,17 @@ RSpec.describe BulkImports::Pipeline do
|
|||
loader BulkImports::Loader, foo: :bar
|
||||
end
|
||||
|
||||
stub_const('BulkImports::MyPipeline', klass)
|
||||
stub_const('BulkImports::TestWikiPipeline', klass)
|
||||
end
|
||||
|
||||
describe 'pipeline attributes' do
|
||||
describe 'getters' do
|
||||
it 'retrieves class attributes' do
|
||||
expect(BulkImports::MyPipeline.get_extractor).to eq({ klass: BulkImports::Extractor, options: { foo: :bar } })
|
||||
expect(BulkImports::MyPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
|
||||
expect(BulkImports::MyPipeline.get_loader).to eq({ klass: BulkImports::Loader, options: { foo: :bar } })
|
||||
expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
|
||||
expect(BulkImports::TestWikiPipeline.get_extractor).to eq({ klass: BulkImports::Extractor, options: { foo: :bar } })
|
||||
expect(BulkImports::TestWikiPipeline.transformers).to contain_exactly({ klass: BulkImports::Transformer, options: { foo: :bar } })
|
||||
expect(BulkImports::TestWikiPipeline.get_loader).to eq({ klass: BulkImports::Loader, options: { foo: :bar } })
|
||||
expect(BulkImports::TestWikiPipeline.abort_on_failure?).to eq(true)
|
||||
expect(BulkImports::TestWikiPipeline.relation).to eq('test_wiki')
|
||||
end
|
||||
|
||||
context 'when extractor and loader are defined within the pipeline' do
|
||||
|
@ -59,23 +60,23 @@ RSpec.describe BulkImports::Pipeline do
|
|||
klass = Class.new
|
||||
options = { test: :test }
|
||||
|
||||
BulkImports::MyPipeline.extractor(klass, options)
|
||||
BulkImports::MyPipeline.transformer(klass, options)
|
||||
BulkImports::MyPipeline.loader(klass, options)
|
||||
BulkImports::MyPipeline.abort_on_failure!
|
||||
BulkImports::MyPipeline.file_extraction_pipeline!
|
||||
BulkImports::TestWikiPipeline.extractor(klass, options)
|
||||
BulkImports::TestWikiPipeline.transformer(klass, options)
|
||||
BulkImports::TestWikiPipeline.loader(klass, options)
|
||||
BulkImports::TestWikiPipeline.abort_on_failure!
|
||||
BulkImports::TestWikiPipeline.file_extraction_pipeline!
|
||||
|
||||
expect(BulkImports::MyPipeline.get_extractor).to eq({ klass: klass, options: options })
|
||||
expect(BulkImports::TestWikiPipeline.get_extractor).to eq({ klass: klass, options: options })
|
||||
|
||||
expect(BulkImports::MyPipeline.transformers)
|
||||
expect(BulkImports::TestWikiPipeline.transformers)
|
||||
.to contain_exactly(
|
||||
{ klass: BulkImports::Transformer, options: { foo: :bar } },
|
||||
{ klass: klass, options: options })
|
||||
|
||||
expect(BulkImports::MyPipeline.get_loader).to eq({ klass: klass, options: options })
|
||||
expect(BulkImports::TestWikiPipeline.get_loader).to eq({ klass: klass, options: options })
|
||||
|
||||
expect(BulkImports::MyPipeline.abort_on_failure?).to eq(true)
|
||||
expect(BulkImports::MyPipeline.file_extraction_pipeline?).to eq(true)
|
||||
expect(BulkImports::TestWikiPipeline.abort_on_failure?).to eq(true)
|
||||
expect(BulkImports::TestWikiPipeline.file_extraction_pipeline?).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -87,7 +88,7 @@ RSpec.describe BulkImports::Pipeline do
|
|||
expect(BulkImports::Transformer).to receive(:new).with(foo: :bar)
|
||||
expect(BulkImports::Loader).to receive(:new).with(foo: :bar)
|
||||
|
||||
pipeline = BulkImports::MyPipeline.new(context)
|
||||
pipeline = BulkImports::TestWikiPipeline.new(context)
|
||||
|
||||
pipeline.send(:extractor)
|
||||
pipeline.send(:transformers)
|
||||
|
|
|
@ -7,6 +7,8 @@ RSpec.describe Gitlab::Ci::JwtV2 do
|
|||
let(:project) { build_stubbed(:project, namespace: namespace) }
|
||||
let(:user) { build_stubbed(:user) }
|
||||
let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') }
|
||||
let(:aud) { described_class::DEFAULT_AUD }
|
||||
|
||||
let(:build) do
|
||||
build_stubbed(
|
||||
:ci_build,
|
||||
|
@ -16,7 +18,7 @@ RSpec.describe Gitlab::Ci::JwtV2 do
|
|||
)
|
||||
end
|
||||
|
||||
subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30) }
|
||||
subject(:ci_job_jwt_v2) { described_class.new(build, ttl: 30, aud: aud) }
|
||||
|
||||
it { is_expected.to be_a Gitlab::Ci::Jwt }
|
||||
|
||||
|
@ -30,5 +32,13 @@ RSpec.describe Gitlab::Ci::JwtV2 do
|
|||
expect(payload[:sub]).to eq("project_path:#{project.full_path}:ref_type:branch:ref:#{pipeline.source_ref}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an aud' do
|
||||
let(:aud) { 'AWS' }
|
||||
|
||||
it 'uses that aud in the payload' do
|
||||
expect(payload[:aud]).to eq('AWS')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -483,6 +483,12 @@ RSpec.describe Gitlab::Git::Repository do
|
|||
it 'displays that branch' do
|
||||
expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch, utf8_branch)
|
||||
end
|
||||
|
||||
context 'when limit is provided' do
|
||||
it 'displays limited number of branches' do
|
||||
expect(repository.branch_names_contains_sha(head_id, limit: 1)).to match_array(['2-mb-file'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#refs_hash" do
|
||||
|
|
|
@ -109,4 +109,24 @@ RSpec.describe Gitlab::GithubImport::Importer::IssuesImporter do
|
|||
.to eq(42)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#increment_object_counter?' do
|
||||
let(:importer) { described_class.new(project, client) }
|
||||
|
||||
context 'when issue is a pull request' do
|
||||
let(:github_issue) { { pull_request: { url: 'some_url' } } }
|
||||
|
||||
it 'returns false' do
|
||||
expect(importer).not_to be_increment_object_counter(github_issue)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue is a regular issue' do
|
||||
let(:github_issue) { {} }
|
||||
|
||||
it 'returns true' do
|
||||
expect(importer).to be_increment_object_counter(github_issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -310,6 +310,7 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
|
|||
describe '#each_object_to_import' do
|
||||
let(:importer) { importer_class.new(project, client) }
|
||||
let(:object) { {} }
|
||||
let(:object_counter_class) { Gitlab::GithubImport::ObjectCounter }
|
||||
|
||||
before do
|
||||
expect(importer)
|
||||
|
@ -335,6 +336,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
|
|||
.with(object)
|
||||
.and_return(false)
|
||||
|
||||
expect(object_counter_class)
|
||||
.to receive(:increment)
|
||||
|
||||
expect(importer)
|
||||
.to receive(:mark_as_imported)
|
||||
.with(object)
|
||||
|
@ -365,6 +369,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
|
|||
.with(object)
|
||||
.and_return(false)
|
||||
|
||||
expect(object_counter_class)
|
||||
.to receive(:increment)
|
||||
|
||||
expect(importer)
|
||||
.to receive(:mark_as_imported)
|
||||
.with(object)
|
||||
|
@ -408,6 +415,9 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
|
|||
.with(object)
|
||||
.and_return(true)
|
||||
|
||||
expect(object_counter_class)
|
||||
.not_to receive(:increment)
|
||||
|
||||
expect(importer)
|
||||
.not_to receive(:mark_as_imported)
|
||||
|
||||
|
@ -464,4 +474,13 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
|
|||
importer.mark_as_imported(object)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#increment_object_counter?' do
|
||||
let(:github_issue) { {} }
|
||||
let(:importer) { importer_class.new(project, client) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(importer).to be_increment_object_counter(github_issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,6 +71,65 @@ RSpec.describe Gitlab::Metrics::System do
|
|||
SNIP
|
||||
end
|
||||
|
||||
let(:mem_info) do
|
||||
# full snapshot
|
||||
<<~SNIP
|
||||
MemTotal: 15362536 kB
|
||||
MemFree: 3403136 kB
|
||||
MemAvailable: 13044528 kB
|
||||
Buffers: 272188 kB
|
||||
Cached: 8171312 kB
|
||||
SwapCached: 0 kB
|
||||
Active: 3332084 kB
|
||||
Inactive: 6981076 kB
|
||||
Active(anon): 1603868 kB
|
||||
Inactive(anon): 9044 kB
|
||||
Active(file): 1728216 kB
|
||||
Inactive(file): 6972032 kB
|
||||
Unevictable: 18676 kB
|
||||
Mlocked: 18676 kB
|
||||
SwapTotal: 0 kB
|
||||
SwapFree: 0 kB
|
||||
Dirty: 6808 kB
|
||||
Writeback: 0 kB
|
||||
AnonPages: 1888300 kB
|
||||
Mapped: 166164 kB
|
||||
Shmem: 12932 kB
|
||||
KReclaimable: 1275120 kB
|
||||
Slab: 1495480 kB
|
||||
SReclaimable: 1275120 kB
|
||||
SUnreclaim: 220360 kB
|
||||
KernelStack: 7072 kB
|
||||
PageTables: 11936 kB
|
||||
NFS_Unstable: 0 kB
|
||||
Bounce: 0 kB
|
||||
WritebackTmp: 0 kB
|
||||
CommitLimit: 7681268 kB
|
||||
Committed_AS: 4976100 kB
|
||||
VmallocTotal: 34359738367 kB
|
||||
VmallocUsed: 25532 kB
|
||||
VmallocChunk: 0 kB
|
||||
Percpu: 23200 kB
|
||||
HardwareCorrupted: 0 kB
|
||||
AnonHugePages: 202752 kB
|
||||
ShmemHugePages: 0 kB
|
||||
ShmemPmdMapped: 0 kB
|
||||
FileHugePages: 0 kB
|
||||
FilePmdMapped: 0 kB
|
||||
CmaTotal: 0 kB
|
||||
CmaFree: 0 kB
|
||||
HugePages_Total: 0
|
||||
HugePages_Free: 0
|
||||
HugePages_Rsvd: 0
|
||||
HugePages_Surp: 0
|
||||
Hugepagesize: 2048 kB
|
||||
Hugetlb: 0 kB
|
||||
DirectMap4k: 4637504 kB
|
||||
DirectMap2M: 11087872 kB
|
||||
DirectMap1G: 2097152 kB
|
||||
SNIP
|
||||
end
|
||||
|
||||
describe '.memory_usage_rss' do
|
||||
context 'without PID' do
|
||||
it "returns the current process' resident set size (RSS) in bytes" do
|
||||
|
@ -125,6 +184,14 @@ RSpec.describe Gitlab::Metrics::System do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.memory_total' do
|
||||
it "returns the current process' resident set size (RSS) in bytes" do
|
||||
mock_existing_proc_file('/proc/meminfo', mem_info)
|
||||
|
||||
expect(described_class.memory_total).to eq(15731236864)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.process_runtime_elapsed_seconds' do
|
||||
it 'returns the seconds elapsed since the process was started' do
|
||||
# sets process starttime ticks to 1000
|
||||
|
|
|
@ -130,9 +130,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
end
|
||||
|
||||
it 'return true when everything is within limit', :aggregate_failures do
|
||||
expect(memory_killer).to receive(:get_rss).and_return(100)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
|
||||
expect(memory_killer).to receive(:get_rss_kb).and_return(100)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
|
||||
expect(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
|
||||
|
||||
expect(memory_killer).to receive(:refresh_state)
|
||||
.with(:running)
|
||||
|
@ -145,9 +146,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
end
|
||||
|
||||
it 'return false when rss exceeds hard_limit_rss', :aggregate_failures do
|
||||
expect(memory_killer).to receive(:get_rss).at_least(:once).and_return(400)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss).at_least(:once).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss).at_least(:once).and_return(300)
|
||||
expect(memory_killer).to receive(:get_rss_kb).at_least(:once).and_return(400)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss_kb).at_least(:once).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss_kb).at_least(:once).and_return(300)
|
||||
expect(memory_killer).to receive(:get_memory_total_kb).at_least(:once).and_return(3072)
|
||||
|
||||
expect(memory_killer).to receive(:refresh_state)
|
||||
.with(:running)
|
||||
|
@ -165,9 +167,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
end
|
||||
|
||||
it 'return false when rss exceed hard_limit_rss after a while', :aggregate_failures do
|
||||
expect(memory_killer).to receive(:get_rss).and_return(250, 400, 400)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss).at_least(:once).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss).at_least(:once).and_return(300)
|
||||
expect(memory_killer).to receive(:get_rss_kb).and_return(250, 400, 400)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss_kb).at_least(:once).and_return(200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss_kb).at_least(:once).and_return(300)
|
||||
expect(memory_killer).to receive(:get_memory_total_kb).at_least(:once).and_return(3072)
|
||||
|
||||
expect(memory_killer).to receive(:refresh_state)
|
||||
.with(:running)
|
||||
|
@ -187,9 +190,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
end
|
||||
|
||||
it 'return true when rss below soft_limit_rss after a while within GRACE_BALLOON_SECONDS', :aggregate_failures do
|
||||
expect(memory_killer).to receive(:get_rss).and_return(250, 100)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss).and_return(200, 200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss).and_return(300, 300)
|
||||
expect(memory_killer).to receive(:get_rss_kb).and_return(250, 100)
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200, 200)
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300, 300)
|
||||
expect(memory_killer).to receive(:get_memory_total_kb).and_return(3072, 3072)
|
||||
|
||||
expect(memory_killer).to receive(:refresh_state)
|
||||
.with(:running)
|
||||
|
@ -211,9 +215,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
let(:grace_balloon_seconds) { 0 }
|
||||
|
||||
it 'return false when rss exceed soft_limit_rss', :aggregate_failures do
|
||||
allow(memory_killer).to receive(:get_rss).and_return(250)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
|
||||
allow(memory_killer).to receive(:get_rss_kb).and_return(250)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
|
||||
allow(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
|
||||
|
||||
expect(memory_killer).to receive(:refresh_state)
|
||||
.with(:running)
|
||||
|
@ -253,9 +258,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
stub_const("#{described_class}::SHUTDOWN_TIMEOUT_SECONDS", shutdown_timeout_seconds)
|
||||
stub_feature_flags(sidekiq_memory_killer_read_only_mode: false)
|
||||
allow(Sidekiq).to receive(:options).and_return(timeout: 9)
|
||||
allow(memory_killer).to receive(:get_rss).and_return(100)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(200)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(300)
|
||||
allow(memory_killer).to receive(:get_rss_kb).and_return(100)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(200)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(300)
|
||||
allow(memory_killer).to receive(:get_memory_total_kb).and_return(3072)
|
||||
end
|
||||
|
||||
it 'send signal' do
|
||||
|
@ -367,6 +373,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
let(:current_rss) { 100 }
|
||||
let(:soft_limit_rss) { 200 }
|
||||
let(:hard_limit_rss) { 300 }
|
||||
let(:memory_total) { 3072 }
|
||||
let(:jid) { 1 }
|
||||
let(:reason) { 'rss out of range reason description' }
|
||||
let(:queue) { 'default' }
|
||||
|
@ -385,9 +392,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
before do
|
||||
stub_const("DummyWorker", worker)
|
||||
|
||||
allow(memory_killer).to receive(:get_rss).and_return(*current_rss)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss).and_return(soft_limit_rss)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss).and_return(hard_limit_rss)
|
||||
allow(memory_killer).to receive(:get_rss_kb).and_return(*current_rss)
|
||||
allow(memory_killer).to receive(:get_soft_limit_rss_kb).and_return(soft_limit_rss)
|
||||
allow(memory_killer).to receive(:get_hard_limit_rss_kb).and_return(hard_limit_rss)
|
||||
allow(memory_killer).to receive(:get_memory_total_kb).and_return(memory_total)
|
||||
|
||||
memory_killer.send(:refresh_state, :running)
|
||||
end
|
||||
|
@ -405,7 +413,8 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
hard_limit_rss: hard_limit_rss,
|
||||
soft_limit_rss: soft_limit_rss,
|
||||
reason: reason,
|
||||
running_jobs: running_jobs)
|
||||
running_jobs: running_jobs,
|
||||
memory_total_kb: memory_total)
|
||||
|
||||
expect(metrics[:sidekiq_memory_killer_running_jobs]).to receive(:increment)
|
||||
.with({ worker_class: "DummyWorker", deadline_exceeded: true })
|
||||
|
@ -541,9 +550,10 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
|
|||
subject { memory_killer.send(:refresh_state, :shutting_down) }
|
||||
|
||||
it 'calls gitlab metrics gauge set methods' do
|
||||
expect(memory_killer).to receive(:get_rss) { 1010 }
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss) { 1020 }
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss) { 1040 }
|
||||
expect(memory_killer).to receive(:get_rss_kb) { 1010 }
|
||||
expect(memory_killer).to receive(:get_soft_limit_rss_kb) { 1020 }
|
||||
expect(memory_killer).to receive(:get_hard_limit_rss_kb) { 1040 }
|
||||
expect(memory_killer).to receive(:get_memory_total_kb) { 3072 }
|
||||
|
||||
expect(metrics[:sidekiq_memory_killer_phase]).to receive(:set)
|
||||
.with({}, described_class::PHASE[:shutting_down])
|
||||
|
|
|
@ -45,30 +45,30 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
|
|||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when split_operations_visibility_permissions FF is disabled' do
|
||||
shared_examples 'split_operations_visibility_permissions FF disabled' do
|
||||
before do
|
||||
stub_feature_flags(split_operations_visibility_permissions: false)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_nil }
|
||||
|
||||
context 'and the feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(split_operations_visibility_permissions: false)
|
||||
project.update_attribute("#{item_id}_access_level", 'disabled')
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_nil }
|
||||
end
|
||||
|
||||
context 'and the feature is disabled' do
|
||||
before do
|
||||
project.update_attribute("#{item_id}_access_level", 'disabled')
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_nil }
|
||||
context 'and operations is disabled' do
|
||||
before do
|
||||
project.update_attribute(:operations_access_level, 'disabled')
|
||||
end
|
||||
|
||||
context 'and operations is disabled' do
|
||||
before do
|
||||
project.update_attribute(:operations_access_level, 'disabled')
|
||||
end
|
||||
|
||||
it do
|
||||
is_expected.to be_nil if [:environments, :feature_flags].include?(item_id)
|
||||
end
|
||||
it do
|
||||
is_expected.to be_nil if [:environments, :feature_flags].include?(item_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -77,12 +77,14 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
|
|||
let(:item_id) { :feature_flags }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
it_behaves_like 'split_operations_visibility_permissions FF disabled'
|
||||
end
|
||||
|
||||
describe 'Environments' do
|
||||
let(:item_id) { :environments }
|
||||
|
||||
it_behaves_like 'access rights checks'
|
||||
it_behaves_like 'split_operations_visibility_permissions FF disabled'
|
||||
end
|
||||
|
||||
describe 'Releases' do
|
||||
|
|
|
@ -3,15 +3,45 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BulkImports::Failure, type: :model do
|
||||
let(:failure) { create(:bulk_import_failure) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:entity).required }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
before do
|
||||
create(:bulk_import_failure)
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of(:entity) }
|
||||
end
|
||||
|
||||
describe '#relation' do
|
||||
context 'when pipeline class is valid' do
|
||||
it 'returns pipeline defined relation' do
|
||||
failure.update!(pipeline_class: 'BulkImports::Common::Pipelines::WikiPipeline')
|
||||
|
||||
expect(failure.relation).to eq('wiki')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline class is invalid' do
|
||||
it 'returns default relation' do
|
||||
failure.update!(pipeline_class: 'foobar')
|
||||
|
||||
expect(failure.relation).to eq('foobar')
|
||||
end
|
||||
|
||||
context 'when pipeline class is outside of BulkImports namespace' do
|
||||
it 'returns default relation' do
|
||||
failure.update!(pipeline_class: 'Gitlab::ImportExport::Importer')
|
||||
|
||||
expect(failure.relation).to eq('importer')
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns demodulized, underscored, chomped string' do
|
||||
failure.update!(pipeline_class: 'BulkImports::Pipelines::Test::TestRelationPipeline')
|
||||
|
||||
expect(failure.relation).to eq('test_relation')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2817,6 +2817,14 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the opt_in_jwt project setting is true' do
|
||||
it 'does not include the JWT variables' do
|
||||
project.ci_cd_settings.update!(opt_in_jwt: true)
|
||||
|
||||
expect(subject.pluck(:key)).not_to include('CI_JOB_JWT', 'CI_JOB_JWT_V1', 'CI_JOB_JWT_V2')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'variables ordering' do
|
||||
context 'when variables hierarchy is stubbed' do
|
||||
let(:build_pre_var) { { key: 'build', value: 'value', public: true, masked: false } }
|
||||
|
@ -3527,6 +3535,49 @@ RSpec.describe Ci::Build do
|
|||
|
||||
it { is_expected.to include(key: job_variable.key, value: job_variable.value, public: false, masked: false) }
|
||||
end
|
||||
|
||||
context 'when ID tokens are defined on the build' do
|
||||
before do
|
||||
rsa_key = OpenSSL::PKey::RSA.generate(3072).to_s
|
||||
stub_application_setting(ci_jwt_signing_key: rsa_key)
|
||||
build.metadata.update!(id_tokens: {
|
||||
'ID_TOKEN_1' => { id_token: { aud: 'developers' } },
|
||||
'ID_TOKEN_2' => { id_token: { aud: 'maintainers' } }
|
||||
})
|
||||
end
|
||||
|
||||
subject(:runner_vars) { build.variables.to_runner_variables }
|
||||
|
||||
it 'includes the ID token variables' do
|
||||
expect(runner_vars).to include(
|
||||
a_hash_including(key: 'ID_TOKEN_1', public: false, masked: true),
|
||||
a_hash_including(key: 'ID_TOKEN_2', public: false, masked: true)
|
||||
)
|
||||
|
||||
id_token_var_1 = runner_vars.find { |var| var[:key] == 'ID_TOKEN_1' }
|
||||
id_token_var_2 = runner_vars.find { |var| var[:key] == 'ID_TOKEN_2' }
|
||||
id_token_1 = JWT.decode(id_token_var_1[:value], nil, false).first
|
||||
id_token_2 = JWT.decode(id_token_var_2[:value], nil, false).first
|
||||
expect(id_token_1['aud']).to eq('developers')
|
||||
expect(id_token_2['aud']).to eq('maintainers')
|
||||
end
|
||||
|
||||
context 'when a NoSigningKeyError is raised' do
|
||||
it 'does not include the ID token variables' do
|
||||
allow(::Gitlab::Ci::JwtV2).to receive(:for_build).and_raise(::Gitlab::Ci::Jwt::NoSigningKeyError)
|
||||
|
||||
expect(runner_vars.map { |var| var[:key] }).not_to include('ID_TOKEN_1', 'ID_TOKEN_2')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a RSAError is raised' do
|
||||
it 'does not include the ID token variables' do
|
||||
allow(::Gitlab::Ci::JwtV2).to receive(:for_build).and_raise(::OpenSSL::PKey::RSAError)
|
||||
|
||||
expect(runner_vars.map { |var| var[:key] }).not_to include('ID_TOKEN_1', 'ID_TOKEN_2')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scoped_variables' do
|
||||
|
|
|
@ -177,7 +177,7 @@ RSpec.describe Ci::Processable do
|
|||
Ci::Build.attribute_names.map(&:to_sym) +
|
||||
Ci::Build.attribute_aliases.keys.map(&:to_sym) +
|
||||
Ci::Build.reflect_on_all_associations.map(&:name) +
|
||||
[:tag_list, :needs_attributes, :job_variables_attributes] -
|
||||
[:tag_list, :needs_attributes, :job_variables_attributes, :id_tokens] -
|
||||
# ToDo: Move EE accessors to ee/
|
||||
::Ci::Build.extra_accessors -
|
||||
[:dast_site_profiles_build, :dast_scanner_profiles_build]
|
||||
|
|
|
@ -40,12 +40,20 @@ RSpec.describe Repository do
|
|||
end
|
||||
|
||||
describe '#branch_names_contains' do
|
||||
subject { repository.branch_names_contains(sample_commit.id) }
|
||||
subject { repository.branch_names_contains(sample_commit.id, **opts) }
|
||||
|
||||
let(:opts) { {} }
|
||||
|
||||
it { is_expected.to include('master') }
|
||||
it { is_expected.not_to include('feature') }
|
||||
it { is_expected.not_to include('fix') }
|
||||
|
||||
context 'when limit is provided' do
|
||||
let(:opts) { { limit: 1 } }
|
||||
|
||||
it { is_expected.to match_array(["'test'"]) }
|
||||
end
|
||||
|
||||
describe 'when storage is broken', :broken_storage do
|
||||
it 'raises a storage error' do
|
||||
expect_to_raise_storage_error do
|
||||
|
@ -56,10 +64,18 @@ RSpec.describe Repository do
|
|||
end
|
||||
|
||||
describe '#tag_names_contains' do
|
||||
subject { repository.tag_names_contains(sample_commit.id) }
|
||||
subject { repository.tag_names_contains(sample_commit.id, **opts) }
|
||||
|
||||
let(:opts) { {} }
|
||||
|
||||
it { is_expected.to include('v1.1.0') }
|
||||
it { is_expected.not_to include('v1.0.0') }
|
||||
|
||||
context 'when limit is provided' do
|
||||
let(:opts) { { limit: 1 } }
|
||||
|
||||
it { is_expected.to match_array(['v1.1.0']) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tags_sorted_by' do
|
||||
|
|
|
@ -602,6 +602,21 @@ RSpec.describe API::GenericPackages do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with access to package registry for everyone' do
|
||||
let_it_be(:user_role) { :anonymous }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
|
||||
end
|
||||
|
||||
it 'responds with success' do
|
||||
download_file(auth_header)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with package status' do
|
||||
where(:package_status, :expected_status) do
|
||||
:default | :success
|
||||
|
|
|
@ -128,7 +128,6 @@ project_feature:
|
|||
- infrastructure_access_level
|
||||
- feature_flags_access_level
|
||||
- environments_access_level
|
||||
- releases_access_level
|
||||
- project_id
|
||||
- updated_at
|
||||
computed_attributes:
|
||||
|
|
|
@ -221,6 +221,16 @@ RSpec.describe API::Projects do
|
|||
expect(project_response['container_registry_enabled']).to eq(false)
|
||||
end
|
||||
|
||||
it 'includes releases_access_level', :aggregate_failures do
|
||||
project.project_feature.update!(releases_access_level: ProjectFeature::DISABLED)
|
||||
|
||||
get api('/projects', user)
|
||||
project_response = json_response.find { |p| p['id'] == project.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(project_response['releases_access_level']).to eq('disabled')
|
||||
end
|
||||
|
||||
context 'when some projects are in a group' do
|
||||
before do
|
||||
create(:project, :public, group: create(:group))
|
||||
|
@ -1171,6 +1181,7 @@ RSpec.describe API::Projects do
|
|||
attrs[:analytics_access_level] = 'disabled'
|
||||
attrs[:container_registry_access_level] = 'private'
|
||||
attrs[:security_and_compliance_access_level] = 'private'
|
||||
attrs[:releases_access_level] = 'disabled'
|
||||
end
|
||||
|
||||
post api('/projects', user), params: project
|
||||
|
@ -1180,7 +1191,7 @@ RSpec.describe API::Projects do
|
|||
project.each_pair do |k, v|
|
||||
next if %i[
|
||||
has_external_issue_tracker has_external_wiki issues_enabled merge_requests_enabled wiki_enabled storage_version
|
||||
container_registry_access_level
|
||||
container_registry_access_level releases_access_level
|
||||
].include?(k)
|
||||
|
||||
expect(json_response[k.to_s]).to eq(v)
|
||||
|
@ -1195,6 +1206,7 @@ RSpec.describe API::Projects do
|
|||
expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
|
||||
expect(project.project_feature.container_registry_access_level).to eq(ProjectFeature::PRIVATE)
|
||||
expect(project.project_feature.security_and_compliance_access_level).to eq(ProjectFeature::PRIVATE)
|
||||
expect(project.project_feature.releases_access_level).to eq(ProjectFeature::DISABLED)
|
||||
end
|
||||
|
||||
it 'assigns container_registry_enabled to project', :aggregate_failures do
|
||||
|
@ -2333,6 +2345,7 @@ RSpec.describe API::Projects do
|
|||
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
|
||||
expect(json_response['operations_access_level']).to be_present
|
||||
expect(json_response['security_and_compliance_access_level']).to be_present
|
||||
expect(json_response['releases_access_level']).to be_present
|
||||
end
|
||||
|
||||
it 'exposes all necessary attributes' do
|
||||
|
@ -2402,6 +2415,7 @@ RSpec.describe API::Projects do
|
|||
expect(json_response['builds_access_level']).to be_present
|
||||
expect(json_response['operations_access_level']).to be_present
|
||||
expect(json_response['security_and_compliance_access_level']).to be_present
|
||||
expect(json_response['releases_access_level']).to be_present
|
||||
expect(json_response).to have_key('emails_disabled')
|
||||
expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions)
|
||||
expect(json_response['remove_source_branch_after_merge']).to be_truthy
|
||||
|
@ -3386,6 +3400,14 @@ RSpec.describe API::Projects do
|
|||
expect(Project.find_by(path: project[:path]).analytics_access_level).to eq(ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it 'sets releases_access_level', :aggregate_failures do
|
||||
put api("/projects/#{project.id}", user), params: { releases_access_level: 'private' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['releases_access_level']).to eq('private')
|
||||
expect(Project.find_by(path: project[:path]).releases_access_level).to eq(ProjectFeature::PRIVATE)
|
||||
end
|
||||
|
||||
it 'returns 400 when nothing sent' do
|
||||
project_param = {}
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
subject { described_class.new(xml: xml, data: data).execute }
|
||||
|
||||
let(:xml) { nil }
|
||||
let(:data) { {} }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::ROOT_TAG", 'test')
|
||||
|
@ -18,5 +21,13 @@ RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
|
|||
expect(result.children.first.attributes['foo1'].value).to eq('bar1')
|
||||
expect(result.children.first.attributes['foo2'].value).to eq('bar2')
|
||||
end
|
||||
|
||||
context 'when call with parameters' do
|
||||
let(:xml) { 'test' }
|
||||
|
||||
it 'raise NotImplementedError' do
|
||||
expect { subject }.to raise_error NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,18 +3,32 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXml do
|
||||
describe '#execute' do
|
||||
subject { described_class.new.execute }
|
||||
subject { described_class.new(xml: xml, data: data).execute }
|
||||
|
||||
context "when generate empty xml" do
|
||||
let(:expected_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
|
||||
XML
|
||||
end
|
||||
let(:empty_xml) do
|
||||
<<~XML
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
|
||||
XML
|
||||
end
|
||||
|
||||
it 'generate expected xml' do
|
||||
expect(subject).to eq(expected_xml)
|
||||
it_behaves_like 'handling rpm xml file'
|
||||
|
||||
context 'when updating existing xml' do
|
||||
include_context 'with rpm package data'
|
||||
|
||||
let(:xml) { empty_xml }
|
||||
let(:data) { xml_update_params }
|
||||
let(:required_text_only_attributes) { %i[description summary arch name] }
|
||||
|
||||
it 'adds node with required_text_only_attributes' do
|
||||
result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
|
||||
|
||||
required_text_only_attributes.each do |attribute|
|
||||
expect(
|
||||
result.at("//#{described_class::ROOT_TAG}/package/#{attribute}").text
|
||||
).to eq(data[attribute])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_context 'with rpm package data' do
|
||||
def xml_update_params
|
||||
Gitlab::Json.parse(fixture_file('packages/rpm/payload.json')).with_indifferent_access
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'handling rpm xml file' do
|
||||
include_context 'with rpm package data'
|
||||
|
||||
let(:xml) { nil }
|
||||
let(:data) { {} }
|
||||
|
||||
context 'when generate empty xml' do
|
||||
it 'generate expected xml' do
|
||||
expect(subject).to eq(empty_xml)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when updating existing xml' do
|
||||
let(:xml) { empty_xml }
|
||||
let(:data) { xml_update_params }
|
||||
|
||||
shared_examples 'changing root tag attribute' do
|
||||
it "increment previous 'packages' value by 1" do
|
||||
previous_value = Nokogiri::XML(xml).at(described_class::ROOT_TAG).attributes["packages"].value.to_i
|
||||
new_value = Nokogiri::XML(subject).at(described_class::ROOT_TAG).attributes["packages"].value.to_i
|
||||
|
||||
expect(previous_value + 1).to eq(new_value)
|
||||
end
|
||||
end
|
||||
|
||||
it 'generate valid xml add expected xml node to existing xml' do
|
||||
# Have one root attribute
|
||||
result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
|
||||
expect(result.children.count).to eq(1)
|
||||
|
||||
# Root node has 1 child with generated node
|
||||
expect(result.xpath("//#{described_class::ROOT_TAG}/package").count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when empty xml' do
|
||||
it_behaves_like 'changing root tag attribute'
|
||||
end
|
||||
|
||||
context 'when xml has children' do
|
||||
let(:xml) { described_class.new(xml: empty_xml, data: data).execute }
|
||||
|
||||
it 'has children nodes' do
|
||||
result = Nokogiri::XML::Document.parse(xml).remove_namespaces!
|
||||
expect(result.children.count).to be > 0
|
||||
end
|
||||
|
||||
it_behaves_like 'changing root tag attribute'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -376,7 +376,7 @@ RSpec.describe BulkImports::PipelineWorker do
|
|||
hash_including(
|
||||
'pipeline_name' => 'NdjsonPipeline',
|
||||
'entity_id' => entity.id,
|
||||
'message' => 'Error!'
|
||||
'message' => 'Export from source instance failed: Error!'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
|
|
@ -195,4 +195,12 @@ RSpec.describe Gitlab::GithubImport::ObjectImporter, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#increment_object_counter?' do
|
||||
let(:issue) { double(:issue, pull_request?: true) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(worker).to be_increment_object_counter(issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,4 +45,15 @@ RSpec.describe Gitlab::GithubImport::ImportIssueWorker do
|
|||
worker.import(project, client, hash)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#increment_object_counter?' do
|
||||
context 'when github issue is a pull request' do
|
||||
let(:issue) { double(:issue, pull_request?: true) }
|
||||
let(:project) { double(:project) }
|
||||
|
||||
it 'returns false' do
|
||||
expect(worker).not_to be_increment_object_counter(issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue