Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-18 12:09:21 +00:00
parent 7bbc9509dc
commit a300f4d5c7
80 changed files with 683 additions and 319 deletions

View File

@ -1,20 +1,6 @@
---
Cop/StaticTranslationDefinition:
Exclude:
- 'app/models/application_setting.rb'
- 'app/models/diff_viewer/image.rb'
- 'app/models/diff_viewer/rich.rb'
- 'app/models/diff_viewer/simple.rb'
- 'app/models/group_group_link.rb'
- 'app/models/jira_import_state.rb'
- 'app/models/member.rb'
- 'app/models/project.rb'
- 'app/models/project_group_link.rb'
- 'app/models/user.rb'
- 'app/models/users/banned_user.rb'
- 'ee/app/models/allowed_email_domain.rb'
- 'ee/app/models/dast/site_profile_secret_variable.rb'
- 'ee/app/models/group_merge_request_approval_setting.rb'
- 'ee/app/models/incident_management/escalation_policy.rb'
- 'ee/app/models/incident_management/escalation_rule.rb'
- 'ee/app/models/vulnerabilities/read.rb'

View File

@ -424,7 +424,7 @@ group :development, :test do
gem 'sigdump', '~> 0.2.4', require: 'sigdump/setup'
gem 'pact', '~> 1.12'
gem 'pact', '~> 1.63'
end
group :development, :test, :danger do

View File

@ -141,6 +141,7 @@
{"name":"ethon","version":"0.15.0","platform":"ruby","checksum":"0809805a035bc10f54162ca99f15ded49e428e0488bcfe1c08c821e18261a74d"},
{"name":"excon","version":"0.90.0","platform":"ruby","checksum":"01beac0f20652b12de95aef931f72bcb82ffb009e1c34c42a5cf5df93f4070ae"},
{"name":"execjs","version":"2.8.1","platform":"ruby","checksum":"6d939919cfd81bcc4d6556f322c3995a70cfe4289ea0bd3b1f999b489c323088"},
{"name":"expgen","version":"0.1.1","platform":"ruby","checksum":"4e6a0f65b210a201d6045debb3e62a24e33251a49f81a11b067d303a60d3a239"},
{"name":"expression_parser","version":"0.9.0","platform":"ruby","checksum":"2b56db3cffc48c3337f4f29f5bc2374c86e7ba29acb40269c74bb55af9f868a4"},
{"name":"extended-markdown-filter","version":"0.6.0","platform":"ruby","checksum":"46844b5740b1703a0e0674e31a17c83d1244a3198abb3aae51cad1eb152eb19e"},
{"name":"factory_bot","version":"6.2.0","platform":"ruby","checksum":"d181902cdda531cf6cef036001b3a700a7b5e04bac63976864530120b2ac7d13"},
@ -398,9 +399,9 @@
{"name":"org-ruby","version":"0.9.12","platform":"ruby","checksum":"93cbec3a4470cb9dca6a4a98dc276a6434ea9d9e7bc2d42ea33c3aedd5d1c974"},
{"name":"orm_adapter","version":"0.5.0","platform":"ruby","checksum":"aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9"},
{"name":"os","version":"1.1.1","platform":"ruby","checksum":"3db1fbc14ab8ea99b69ed8e353c894613e1b35e665fffb90414996cf8989d489"},
{"name":"pact","version":"1.59.0","platform":"ruby","checksum":"6272cea35e4ee809493fadcba9800d4a24c262ef0778a0d1ba5d9a9b3f61fc59"},
{"name":"pact-mock_service","version":"3.6.2","platform":"ruby","checksum":"cc91229484ae428b6eb3a6673c178046cbf6610ee6536ca6cbac060b6071f547"},
{"name":"pact-support","version":"1.15.1","platform":"ruby","checksum":"c364596fe9fe78c4f93028013262d5d97867a680fa6acc35dda946447cdf1d1f"},
{"name":"pact","version":"1.63.0","platform":"ruby","checksum":"cc2991ed242bf182c6a4abadfd492b2923d09a9b3ed24578126cc056921cb151"},
{"name":"pact-mock_service","version":"3.10.0","platform":"ruby","checksum":"898ec3b8d96f1934d15941c701ca7d5fef5ccff32022d9a196fb82073cd95e27"},
{"name":"pact-support","version":"1.18.1","platform":"ruby","checksum":"4a25961c8b1c4132e433a8eaa838b1e6914c6d3aae48eee705b9860a5e8b0476"},
{"name":"parallel","version":"1.22.1","platform":"ruby","checksum":"ebdf1f0c51f182df38522f70ba770214940bef998cdb6e00f36492b29699761f"},
{"name":"parser","version":"3.1.2.1","platform":"ruby","checksum":"57e49821b52d5fe7baffaca44ed77e9754688c9bbc68443b5293a722fdb161e0"},
{"name":"parslet","version":"1.8.2","platform":"ruby","checksum":"08d1ab3721cd3f175bfbee8788b2ddff71f92038f2d69bd65454c22bb9fbd98a"},
@ -438,7 +439,7 @@
{"name":"rack-oauth2","version":"1.21.3","platform":"ruby","checksum":"4e72a79dd6a866692e84422a552b27c38a5a1918ded06661e04910f2bbe676ba"},
{"name":"rack-protection","version":"2.2.2","platform":"ruby","checksum":"fd41414dbabbec274af0bdb1f72a48504449de4d979782c9af38cbb5dfff3299"},
{"name":"rack-proxy","version":"0.7.4","platform":"ruby","checksum":"a8bb373583d8a3165d8caf5af5fd7c32c9e8a91b983fbc531efa0e3d6617d2d4"},
{"name":"rack-test","version":"1.1.0","platform":"ruby","checksum":"154161f40f162b1c009a655b7b0c5de3a3102cc6d7d2e94b64e1f46ace800866"},
{"name":"rack-test","version":"2.0.2","platform":"ruby","checksum":"adadd0e957f63a34199a9fdf905a920a0b0a50795735095b4ac4bd3c13385466"},
{"name":"rack-timeout","version":"0.6.3","platform":"ruby","checksum":"1754892eacc124d405e7f1145731ec9b7421ebd1bee5d51ddc18b72c204d0ab3"},
{"name":"rails","version":"6.1.6.1","platform":"ruby","checksum":"17024921a3913fb341f584542b06adf6bb12977a8b92d5fce093c3996c963686"},
{"name":"rails-controller-testing","version":"1.0.5","platform":"ruby","checksum":"741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94"},
@ -448,7 +449,6 @@
{"name":"railties","version":"6.1.6.1","platform":"ruby","checksum":"bafecdf2dcbe4ea44e1ab7081fd797aa87ae9bbcd0f3a4372b662a1b93949733"},
{"name":"rainbow","version":"3.1.1","platform":"ruby","checksum":"039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a"},
{"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"},
{"name":"randexp","version":"0.1.7","platform":"ruby","checksum":"3026510ecf6a8e8642b9b96fa44bb41af6d24058023b7df77cf280f08e14e4c8"},
{"name":"rb-fsevent","version":"0.11.2","platform":"ruby","checksum":"43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe"},
{"name":"rb-inotify","version":"0.10.1","platform":"ruby","checksum":"050062d4f31d307cca52c3f6a7f4b946df8de25fc4bd373e1a5142e41034a7ca"},
{"name":"rbtrace","version":"0.4.14","platform":"ruby","checksum":"162bbf89cecabfc4f09c869b655f6f3a679c4870ebb7cbdcadf7393a81cc1769"},
@ -586,7 +586,7 @@
{"name":"timecop","version":"0.9.1","platform":"ruby","checksum":"374b543f0961dbd487e96d09ac812d4fdfeb603ec705bbff241ba060d0a9f534"},
{"name":"timeliness","version":"0.3.10","platform":"ruby","checksum":"c357233ce19dc53148e8b29dfddde134689f18f52b32928e9dfe12ebcf4a773f"},
{"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"},
{"name":"tins","version":"1.31.0","platform":"ruby","checksum":"20b5ea997dc046358fd05f15d39636bd7946798591b9c5741cc41f69853c7894"},
{"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"},
{"name":"toml-rb","version":"2.0.1","platform":"ruby","checksum":"5016c6c77ac72bca5fe67c372722bdfdd4479a6fe1a1c4ff2a486e247849b274"},
{"name":"tomlrb","version":"1.3.0","platform":"ruby","checksum":"68666bf53fa70ba686a48a7435ce7e086f5227c58c4c993bd9792f4760f2a503"},
{"name":"tpm-key_attestation","version":"0.9.0","platform":"ruby","checksum":"e469ad9111a68dab4d04596e1c0621d7c877c2e3e247f765af3c04f1adf2b8cd"},

View File

@ -420,6 +420,8 @@ GEM
ffi (>= 1.15.0)
excon (0.90.0)
execjs (2.8.1)
expgen (0.1.1)
parslet
expression_parser (0.9.0)
extended-markdown-filter (0.6.0)
html-pipeline (~> 2.0)
@ -999,29 +1001,29 @@ GEM
rubypants (~> 0.2)
orm_adapter (0.5.0)
os (1.1.1)
pact (1.59.0)
pact (1.63.0)
pact-mock_service (~> 3.0, >= 3.3.1)
pact-support (~> 1.15)
rack-test (>= 0.6.3, < 2.0.0)
pact-support (~> 1.16, >= 1.16.9)
rack-test (>= 0.6.3, < 3.0.0)
rspec (~> 3.0)
term-ansicolor (~> 1.0)
thor (>= 0.20, < 2.0)
webrick (~> 1.3)
pact-mock_service (3.6.2)
pact-mock_service (3.10.0)
filelock (~> 1.1)
find_a_port (~> 1.0.1)
json
pact-support (~> 1.12, >= 1.12.0)
pact-support (~> 1.16, >= 1.16.4)
rack (~> 2.0)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
thor (>= 0.19, < 2.0)
webrick (~> 1.3)
pact-support (1.15.1)
awesome_print (~> 1.1)
randexp (~> 0.1.7)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
pact-support (1.18.1)
awesome_print (~> 1.9)
diff-lcs (~> 1.4)
expgen (~> 0.1)
rainbow (~> 3.1.1)
parallel (1.22.1)
parser (3.1.2.1)
ast (~> 2.4.1)
@ -1087,8 +1089,8 @@ GEM
rack
rack-proxy (0.7.4)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-test (2.0.2)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (6.1.6.1)
actioncable (= 6.1.6.1)
@ -1125,7 +1127,6 @@ GEM
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.6)
randexp (0.1.7)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
@ -1415,7 +1416,7 @@ GEM
timecop (0.9.1)
timeliness (0.3.10)
timfel-krb5-auth (0.8.3)
tins (1.31.0)
tins (1.31.1)
sync
toml-rb (2.0.1)
citrus (~> 3.0, > 3.0)
@ -1725,7 +1726,7 @@ DEPENDENCIES
omniauth_crowd (~> 2.4.0)!
openssl (= 2.2.1)
org-ruby (~> 0.9.12)
pact (~> 1.12)
pact (~> 1.63)
parallel (~> 1.19)
parslet (~> 1.8)
peek (~> 1.1)

View File

@ -472,7 +472,7 @@ export default {
fetchData(toggleTree = true) {
this.fetchDiffFilesMeta()
.then(({ real_size = 0 }) => {
this.diffFilesLength = parseInt(real_size, 10);
this.diffFilesLength = parseInt(real_size, 10) || 0;
if (toggleTree) {
this.setTreeDisplay();
}

View File

@ -19,34 +19,35 @@ export default {
components: { GlTabs, GlTab, GroupsApp, GlSearchBoxByType, GlSorting, GlSortingItem },
inject: ['endpoints', 'initialSort'],
data() {
const tabs = [
{
title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
renderEmptyState: true,
lazy: this.$route.name !== ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
store: new GroupsStore({ showSchemaMarkup: true }),
},
{
title: this.$options.i18n[ACTIVE_TAB_SHARED],
key: ACTIVE_TAB_SHARED,
renderEmptyState: false,
lazy: this.$route.name !== ACTIVE_TAB_SHARED,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]),
store: new GroupsStore(),
},
{
title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
key: ACTIVE_TAB_ARCHIVED,
renderEmptyState: false,
lazy: this.$route.name !== ACTIVE_TAB_ARCHIVED,
service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]),
store: new GroupsStore(),
},
];
return {
tabs: [
{
title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
renderEmptyState: true,
lazy: false,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
store: new GroupsStore({ showSchemaMarkup: true }),
},
{
title: this.$options.i18n[ACTIVE_TAB_SHARED],
key: ACTIVE_TAB_SHARED,
renderEmptyState: false,
lazy: true,
service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]),
store: new GroupsStore(),
},
{
title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
key: ACTIVE_TAB_ARCHIVED,
renderEmptyState: false,
lazy: true,
service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]),
store: new GroupsStore(),
},
],
activeTabIndex: 0,
tabs,
activeTabIndex: tabs.findIndex((tab) => tab.key === this.$route.name),
sort: SORTING_ITEM_NAME,
isAscending: true,
search: '',
@ -75,14 +76,6 @@ export default {
) || SORTING_ITEM_NAME;
this.sort = sort;
this.isAscending = sort.asc === sortQueryStringValue;
const activeTabIndex = this.tabs.findIndex((tab) => tab.key === this.$route.name);
if (activeTabIndex === -1) {
return;
}
this.activeTabIndex = activeTabIndex;
},
methods: {
handleTabInput(tabIndex) {

View File

@ -57,7 +57,7 @@ export default {
this.status = res.data.severity;
this.track('rendered_version_badge', {
label: this.status,
label: this.title,
});
}
})
@ -66,19 +66,24 @@ export default {
this.status = null;
});
},
onClick() {
this.track('click_version_badge', { label: this.title });
},
},
UPGRADE_DOCS_URL,
};
</script>
<template>
<gl-badge
v-if="status"
:href="$options.UPGRADE_DOCS_URL"
class="version-check-badge"
:variant="status"
:size="size"
@click="track('click_version_badge', { label: status })"
>{{ title }}</gl-badge
>
<!-- TODO: remove the span element once bootstrap-vue is updated to version 2.21.1 -->
<!-- TODO: https://github.com/bootstrap-vue/bootstrap-vue/issues/6219 -->
<span v-if="status" data-testid="badge-click-wrapper" @click="onClick">
<gl-badge
:href="$options.UPGRADE_DOCS_URL"
class="version-check-badge"
:variant="status"
:size="size"
>{{ title }}</gl-badge
>
</span>
</template>

View File

@ -25,12 +25,21 @@ module Mutations
def resolve(id:)
job = authorized_find!(id: id)
result = ::Ci::JobArtifacts::DestroyBatchService.new(job.job_artifacts, pick_up_at: Time.current).execute
{
job: job,
destroyed_artifacts_count: result[:destroyed_artifacts_count],
errors: Array(result[:errors])
}
result = ::Ci::JobArtifacts::DeleteService.new(job).execute
if result.success?
{
job: job,
destroyed_artifacts_count: result.payload[:destroyed_artifacts_count],
errors: Array(result.payload[:errors])
}
else
{
job: job,
destroyed_artifacts_count: 0,
errors: Array(result.message)
}
end
end
end
end

View File

@ -120,7 +120,7 @@ class ApplicationSetting < ApplicationRecord
if: :help_page_support_url_column_exists?
validates :help_page_documentation_base_url,
length: { maximum: 255, message: _("is too long (maximum is %{count} characters)") },
length: { maximum: 255, message: N_("is too long (maximum is %{count} characters)") },
allow_blank: true,
addressable_url: true
@ -148,7 +148,7 @@ class ApplicationSetting < ApplicationRecord
if: :akismet_enabled
validates :spam_check_api_key,
length: { maximum: 2000, message: _('is too long (maximum is %{count} characters)') },
length: { maximum: 2000, message: N_('is too long (maximum is %{count} characters)') },
allow_blank: true
validates :unique_ips_limit_per_user,
@ -228,7 +228,7 @@ class ApplicationSetting < ApplicationRecord
validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_expiration_policies_enable_historic_entries,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :container_registry_token_expire_delay,
presence: true,
@ -320,8 +320,8 @@ class ApplicationSetting < ApplicationRecord
validates :personal_access_token_prefix,
format: { with: %r{\A[a-zA-Z0-9_+=/@:.-]+\z},
message: _("can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'") },
length: { maximum: 20, message: _('is too long (maximum is %{count} characters)') },
message: N_("can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'") },
length: { maximum: 20, message: N_('is too long (maximum is %{count} characters)') },
allow_blank: true
validates :commit_email_hostname, format: { with: /\A[^@]+\z/ }
@ -369,7 +369,7 @@ class ApplicationSetting < ApplicationRecord
validates :email_restrictions, untrusted_regexp: true
validates :hashed_storage_enabled, inclusion: { in: [true], message: _("Hashed storage can't be disabled anymore for new projects") }
validates :hashed_storage_enabled, inclusion: { in: [true], message: N_("Hashed storage can't be disabled anymore for new projects") }
validates :container_registry_delete_tags_service_timeout,
:container_registry_cleanup_tags_service_max_list_size,
@ -377,7 +377,7 @@ class ApplicationSetting < ApplicationRecord
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :container_registry_expiration_policies_caching,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :container_registry_import_max_tags_count,
:container_registry_import_max_retries,
@ -404,11 +404,11 @@ class ApplicationSetting < ApplicationRecord
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :invisible_captcha_enabled,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :invitation_flow_enforcement, :can_create_group,
allow_nil: false,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
Gitlab::SSHPublicKey.supported_types.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
@ -513,11 +513,11 @@ class ApplicationSetting < ApplicationRecord
rsa_key: true, allow_nil: true
validates :rate_limiting_response_text,
length: { maximum: 255, message: _('is too long (maximum is %{count} characters)') },
length: { maximum: 255, message: N_('is too long (maximum is %{count} characters)') },
allow_blank: true
validates :jira_connect_application_key,
length: { maximum: 255, message: _('is too long (maximum is %{count} characters)') },
length: { maximum: 255, message: N_('is too long (maximum is %{count} characters)') },
allow_blank: true
with_options(presence: true, numericality: { only_integer: true, greater_than: 0 }) do
@ -561,7 +561,7 @@ class ApplicationSetting < ApplicationRecord
allow_nil: false
validates :admin_mode,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :external_pipeline_validation_service_url,
addressable_url: true, allow_blank: true
@ -574,7 +574,7 @@ class ApplicationSetting < ApplicationRecord
inclusion: { in: ApplicationSetting.whats_new_variants.keys }
validates :floc_enabled,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
enum sidekiq_job_limiter_mode: {
Gitlab::SidekiqMiddleware::SizeLimiter::Validator::TRACK_MODE => 0,
@ -589,7 +589,7 @@ class ApplicationSetting < ApplicationRecord
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :sentry_enabled,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :sentry_dsn,
addressable_url: true, presence: true, length: { maximum: 255 },
if: :sentry_enabled?
@ -601,7 +601,7 @@ class ApplicationSetting < ApplicationRecord
if: :sentry_enabled?
validates :error_tracking_enabled,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
validates :error_tracking_api_url,
presence: true,
addressable_url: true,
@ -670,7 +670,7 @@ class ApplicationSetting < ApplicationRecord
attr_encrypted :jitsu_administrator_password, encryption_options_base_32_aes_256_gcm
validates :disable_feed_token,
inclusion: { in: [true, false], message: _('must be a boolean value') }
inclusion: { in: [true, false], message: N_('must be a boolean value') }
before_validation :ensure_uuid!
before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed?

View File

@ -23,7 +23,7 @@ module Ci
def includes?(target_project)
# if the setting is disabled any project is considered to be in scope.
return true unless source_project.ci_job_token_scope_enabled?
return true unless source_project.ci_outbound_job_token_scope_enabled?
target_project.id == source_project.id ||
Ci::JobToken::ProjectScopeLink.from_project(source_project).to_project(target_project).exists?

View File

@ -8,7 +8,7 @@ class GroupGroupLink < ApplicationRecord
validates :shared_group, presence: true
validates :shared_group_id, uniqueness: { scope: [:shared_with_group_id],
message: _('The group has already been shared with this group') }
message: N_('The group has already been shared with this group') }
validates :shared_with_group, presence: true
validates :group_access, inclusion: { in: Gitlab::Access.all_values },
presence: true

View File

@ -21,6 +21,11 @@ module IncidentManagement
validates :note, presence: true, length: { maximum: 10_000 }
validates :note_html, length: { maximum: 10_000 }
has_many :timeline_event_tag_links, class_name: 'IncidentManagement::TimelineEventTagLink'
has_many :timeline_event_tags,
class_name: 'IncidentManagement::TimelineEventTag',
through: :timeline_event_tag_links
scope :order_occurred_at_asc_id_asc, -> { reorder(occurred_at: :asc, id: :asc) }
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module IncidentManagement
class TimelineEventTag < ApplicationRecord
self.table_name = 'incident_management_timeline_event_tags'
belongs_to :project, inverse_of: :incident_management_timeline_event_tags
has_many :timeline_event_tag_links,
class_name: 'IncidentManagement::TimelineEventTagLink'
has_many :timeline_events,
class_name: 'IncidentManagement::TimelineEvent',
through: :timeline_event_tag_links
validates :name, presence: true, format: { with: /\A[^,]+\z/ }
validates :name, uniqueness: { scope: :project_id }
validates :name, length: { maximum: 255 }
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module IncidentManagement
class TimelineEventTagLink < ApplicationRecord
self.table_name = 'incident_management_timeline_event_tag_links'
belongs_to :timeline_event_tag, class_name: 'IncidentManagement::TimelineEventTag'
belongs_to :timeline_event, class_name: 'IncidentManagement::TimelineEvent'
end
end

View File

@ -24,7 +24,7 @@ class JiraImportState < ApplicationRecord
validates :project, uniqueness: {
conditions: -> { where.not(status: STATUSES.values_at(:failed, :finished)) },
message: _('Cannot have multiple Jira imports running at the same time')
message: N_('Cannot have multiple Jira imports running at the same time')
}
before_save :ensure_error_message_size

View File

@ -55,7 +55,7 @@ class Member < ApplicationRecord
validate :signup_email_valid?, on: :create, if: ->(member) { member.invite_email.present? }
validates :user_id,
uniqueness: {
message: _('project bots cannot be added to other groups / projects')
message: N_('project bots cannot be added to other groups / projects')
},
if: :project_bot?
validate :access_level_inclusion

View File

@ -262,6 +262,7 @@ class Project < ApplicationRecord
has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues
has_many :incident_management_issuable_escalation_statuses, through: :issues, inverse_of: :project, class_name: 'IncidentManagement::IssuableEscalationStatus'
has_many :incident_management_timeline_event_tags, inverse_of: :project, class_name: 'IncidentManagement::TimelineEventTag'
has_many :labels, class_name: 'ProjectLabel'
has_many :integrations
has_many :events
@ -476,7 +477,7 @@ class Project < ApplicationRecord
delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true
delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :forward_deployment_enabled, :forward_deployment_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci_outbound, allow_nil: true
delegate :inbound_job_token_scope_enabled, :inbound_job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true
delegate :opt_in_jwt, :opt_in_jwt=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
@ -503,7 +504,7 @@ class Project < ApplicationRecord
validates :description, length: { maximum: 2000 }, allow_blank: true
validates :ci_config_path,
format: { without: %r{(\.{2}|\A/)},
message: _('cannot include leading slash or directory traversal.') },
message: N_('cannot include leading slash or directory traversal.') },
length: { maximum: 255 },
allow_blank: true
validates :name,
@ -699,13 +700,13 @@ class Project < ApplicationRecord
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
chronic_duration_attr :build_timeout_human_readable, :build_timeout,
default: 3600, error_message: _('Maximum job timeout has a value which could not be accepted')
default: 3600, error_message: N_('Maximum job timeout has a value which could not be accepted')
validates :build_timeout, allow_nil: true,
numericality: { greater_than_or_equal_to: 10.minutes,
less_than: MAX_BUILD_TIMEOUT,
only_integer: true,
message: _('needs to be between 10 minutes and 1 month') }
message: N_('needs to be between 10 minutes and 1 month') }
# Used by Projects::CleanupService to hold a map of rewritten object IDs
mount_uploader :bfg_object_map, AttachmentUploader
@ -2896,7 +2897,7 @@ class Project < ApplicationRecord
ci_cd_settings.allow_fork_pipelines_to_run_in_parent_project?
end
def ci_job_token_scope_enabled?
def ci_outbound_job_token_scope_enabled?
return false unless ci_cd_settings
ci_cd_settings.job_token_scope_enabled?

View File

@ -9,7 +9,7 @@ class ProjectGroupLink < ApplicationRecord
validates :project_id, presence: true
validates :group, presence: true
validates :group_id, uniqueness: { scope: [:project_id], message: _("already shared with this group") }
validates :group_id, uniqueness: { scope: [:project_id], message: N_("already shared with this group") }
validates :group_access, presence: true
validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true
validate :different_group

View File

@ -286,10 +286,10 @@ class User < ApplicationRecord
validate :check_username_format, if: :username_changed?
validates :theme_id, allow_nil: true, inclusion: { in: Gitlab::Themes.valid_ids,
message: _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } }
message: ->(*) { _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } } }
validates :color_scheme_id, allow_nil: true, inclusion: { in: Gitlab::ColorSchemes.valid_ids,
message: _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } }
message: ->(*) { _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } } }
validates :website_url, allow_blank: true, url: true, if: :website_url_changed?

View File

@ -7,6 +7,6 @@ module Users
belongs_to :user
validates :user, presence: true
validates :user_id, uniqueness: { message: _("banned user already exists") }
validates :user_id, uniqueness: { message: N_("banned user already exists") }
end
end

View File

@ -15,13 +15,23 @@ module Ci
method: 'Ci::JobArtifacts::DeleteService#execute',
project_id: build.project_id
)
return ServiceResponse.error(
message: 'Action temporarily disabled. The project this job belongs to is undergoing stats refresh.',
reason: :project_stats_refresh
)
end
# fix_expire_at is false because in this case we want to explicitly delete the job artifacts
# this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833
Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable, fix_expire_at: false).execute
result = Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable).execute
ServiceResponse.success
if result.fetch(:status) == :success
ServiceResponse.success(payload:
{
destroyed_artifacts_count: result.fetch(:destroyed_artifacts_count),
statistics_updates: result.fetch(:statistics_updates)
})
else
ServiceResponse.error(message: result.fetch(:message))
end
end
private

View File

@ -9,5 +9,5 @@
= label_tag :password
= password_field_tag :password, nil, disabled: true, class: "form-control gl-form-input bottom", title: title
.form-group
= button_tag _("Sign in"), disabled: true, class: "btn gl-button btn-confirm", type: "button", title: title
= render Pajamas::ButtonComponent.new(variant: :confirm, disabled: true, button_options: { title: title }) do
= _("Sign in")

View File

@ -19,7 +19,7 @@
%input.js-add-list{ type: "checkbox", name: "add_list", checked: add_list }
%span= _('Add list')
.clearfix
%button.gl-button.btn.btn-confirm.float-left.js-new-label-btn{ type: "button" }
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'float-left js-new-label-btn' }) do
= _('Create')
%button.gl-button.btn.btn-default.float-right.js-cancel-label-btn{ type: "button" }
= render Pajamas::ButtonComponent.new(button_options: { class: 'float-right js-cancel-label-btn' }) do
= _('Cancel')

View File

@ -10,6 +10,7 @@ module Ci
idempotent!
deduplicate :until_executed
urgency :high
loggable_arguments 1
def perform(pipeline_id, auto_canceled_by_pipeline_id)
::Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|

View File

@ -1,7 +1,7 @@
---
data_category: optional
key_path: redis_hll_counters.deploy_token_packages.deploy_token_packages_total_unique_counts_monthly
description: A monthly count of packages published to the registry using a deploy
description: A monthly count of unique users who published packages to the registry using a deploy
token
product_section: ops
product_stage: package
@ -11,8 +11,11 @@ value_type: number
status: active
time_frame: 28d
data_source: redis_hll
instrumentation_class: RedisHLLMetric
instrumentation_class: AggregatedMetric
options:
aggregate:
operator: OR
attribute: user_id
events:
- i_package_composer_deploy_token
- i_package_conan_deploy_token

View File

@ -1,7 +1,8 @@
---
data_category: optional
key_path: redis_hll_counters.deploy_token_packages.deploy_token_packages_total_unique_counts_weekly
description: A weekly count of packages published to the registry using a deploy token
description: A weekly count of unique users who published packages to the registry using a deploy
token
product_section: ops
product_stage: package
product_group: package
@ -10,8 +11,11 @@ value_type: number
status: active
time_frame: 7d
data_source: redis_hll
instrumentation_class: RedisHLLMetric
instrumentation_class: AggregatedMetric
options:
aggregate:
operator: OR
attribute: user_id
events:
- i_package_composer_deploy_token
- i_package_conan_deploy_token

View File

@ -0,0 +1,9 @@
---
table_name: incident_management_timeline_event_tag_links
classes:
- IncidentManagement::TimelineEventTagLink
feature_categories:
- incident_management
description: Persists links between timeline event tags and timeline events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271
milestone: '15.6'

View File

@ -0,0 +1,9 @@
---
table_name: incident_management_timeline_event_tags
classes:
- IncidentManagement::TimelineEventTag
feature_categories:
- incident_management
description: Persists tags for timeline events in a project.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271
milestone: '15.6'

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class CreateIncidentManagementTimelineEventTags < Gitlab::Database::Migration[2.0]
enable_lock_retries!
def up
create_table :incident_management_timeline_event_tags do |t|
t.timestamps_with_timezone null: false
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }
t.text :name, limit: 255, null: false
t.index [:project_id, :name], unique: true, name: 'index_im_timeline_event_tags_name_project_id'
end
end
def down
drop_table :incident_management_timeline_event_tags
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
class CreateIncidentManagementTimelineEventTagLinks < Gitlab::Database::Migration[2.0]
enable_lock_retries!
def up
create_table :incident_management_timeline_event_tag_links do |t|
t.references :timeline_event,
null: false,
index: { name: 'index_im_timeline_event_id' },
foreign_key: { to_table: :incident_management_timeline_events, column: :timeline_event_id, on_delete: :cascade }
t.references :timeline_event_tag,
null: false,
index: false,
foreign_key: {
to_table: :incident_management_timeline_event_tags,
column: :timeline_event_tag_id,
on_delete: :cascade
}
t.index [:timeline_event_tag_id, :timeline_event_id],
unique: true,
name: 'index_im_timeline_event_tags_on_tag_id_and_event_id'
t.datetime_with_timezone :created_at, null: false
end
end
def down
drop_table :incident_management_timeline_event_tag_links
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class MembersRemoveMemberNamespaceIdNotNullConstraint < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
CONSTRAINT_NAME = 'check_508774aac0'
def up
remove_not_null_constraint :members, :member_namespace_id, constraint_name: CONSTRAINT_NAME
end
def down
add_not_null_constraint :members, :member_namespace_id, validate: false, constraint_name: CONSTRAINT_NAME
end
end

View File

@ -0,0 +1 @@
6b90dfb738c597a45ecaae792e97e1ae0decb93779ecc35fbc2fbaedafb5b9d1

View File

@ -0,0 +1 @@
ab93968543b6aec0bc304a2c0dc051f63a29b4765df11432fba45325e5e75e55

View File

@ -0,0 +1 @@
25030e3ba7c6632fa86100c2db320d1e0c431f992e2b3e333a98e7e03bd31a49

View File

@ -16411,6 +16411,40 @@ CREATE SEQUENCE incident_management_pending_issue_escalations_id_seq
ALTER SEQUENCE incident_management_pending_issue_escalations_id_seq OWNED BY incident_management_pending_issue_escalations.id;
CREATE TABLE incident_management_timeline_event_tag_links (
id bigint NOT NULL,
timeline_event_id bigint NOT NULL,
timeline_event_tag_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL
);
CREATE SEQUENCE incident_management_timeline_event_tag_links_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE incident_management_timeline_event_tag_links_id_seq OWNED BY incident_management_timeline_event_tag_links.id;
CREATE TABLE incident_management_timeline_event_tags (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
project_id bigint NOT NULL,
name text NOT NULL,
CONSTRAINT check_8717184e2c CHECK ((char_length(name) <= 255))
);
CREATE SEQUENCE incident_management_timeline_event_tags_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE incident_management_timeline_event_tags_id_seq OWNED BY incident_management_timeline_event_tags.id;
CREATE TABLE incident_management_timeline_events (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -23743,6 +23777,10 @@ ALTER TABLE ONLY incident_management_pending_alert_escalations ALTER COLUMN id S
ALTER TABLE ONLY incident_management_pending_issue_escalations ALTER COLUMN id SET DEFAULT nextval('incident_management_pending_issue_escalations_id_seq'::regclass);
ALTER TABLE ONLY incident_management_timeline_event_tag_links ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_event_tag_links_id_seq'::regclass);
ALTER TABLE ONLY incident_management_timeline_event_tags ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_event_tags_id_seq'::regclass);
ALTER TABLE ONLY incident_management_timeline_events ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_events_id_seq'::regclass);
ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statuses_id_seq'::regclass);
@ -25102,9 +25140,6 @@ ALTER TABLE ONLY chat_teams
ALTER TABLE vulnerability_scanners
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
ALTER TABLE members
ADD CONSTRAINT check_508774aac0 CHECK ((member_namespace_id IS NOT NULL)) NOT VALID;
ALTER TABLE sprints
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
@ -25702,6 +25737,12 @@ ALTER TABLE ONLY incident_management_pending_alert_escalations
ALTER TABLE ONLY incident_management_pending_issue_escalations
ADD CONSTRAINT incident_management_pending_issue_escalations_pkey PRIMARY KEY (id, process_at);
ALTER TABLE ONLY incident_management_timeline_event_tag_links
ADD CONSTRAINT incident_management_timeline_event_tag_links_pkey PRIMARY KEY (id);
ALTER TABLE ONLY incident_management_timeline_event_tags
ADD CONSTRAINT incident_management_timeline_event_tags_pkey PRIMARY KEY (id);
ALTER TABLE ONLY incident_management_timeline_events
ADD CONSTRAINT incident_management_timeline_events_pkey PRIMARY KEY (id);
@ -29054,6 +29095,12 @@ CREATE INDEX index_im_issuable_escalation_statuses_on_policy_id ON incident_mana
CREATE UNIQUE INDEX index_im_oncall_schedules_on_project_id_and_iid ON incident_management_oncall_schedules USING btree (project_id, iid);
CREATE INDEX index_im_timeline_event_id ON incident_management_timeline_event_tag_links USING btree (timeline_event_id);
CREATE UNIQUE INDEX index_im_timeline_event_tags_name_project_id ON incident_management_timeline_event_tags USING btree (project_id, name);
CREATE UNIQUE INDEX index_im_timeline_event_tags_on_tag_id_and_event_id ON incident_management_timeline_event_tag_links USING btree (timeline_event_tag_id, timeline_event_id);
CREATE INDEX index_im_timeline_events_author_id ON incident_management_timeline_events USING btree (author_id);
CREATE INDEX index_im_timeline_events_issue_id ON incident_management_timeline_events USING btree (issue_id);
@ -33925,6 +33972,9 @@ ALTER TABLE ONLY issue_user_mentions
ALTER TABLE ONLY merge_request_assignees
ADD CONSTRAINT fk_rails_579d375628 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_timeline_event_tag_links
ADD CONSTRAINT fk_rails_57baccd7f9 FOREIGN KEY (timeline_event_id) REFERENCES incident_management_timeline_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_debian_project_architectures
ADD CONSTRAINT fk_rails_5808663adf FOREIGN KEY (distribution_id) REFERENCES packages_debian_project_distributions(id) ON DELETE CASCADE;
@ -34129,6 +34179,9 @@ ALTER TABLE ONLY group_crm_settings
ALTER TABLE ONLY clusters_applications_ingress
ADD CONSTRAINT fk_rails_753a7b41c1 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_timeline_event_tag_links
ADD CONSTRAINT fk_rails_753b8b6ee3 FOREIGN KEY (timeline_event_tag_id) REFERENCES incident_management_timeline_event_tags(id) ON DELETE CASCADE;
ALTER TABLE ONLY release_links
ADD CONSTRAINT fk_rails_753be7ae29 FOREIGN KEY (release_id) REFERENCES releases(id) ON DELETE CASCADE;
@ -34756,6 +34809,9 @@ ALTER TABLE ONLY deployment_merge_requests
ALTER TABLE ONLY packages_debian_group_component_files
ADD CONSTRAINT fk_rails_dd262386e9 FOREIGN KEY (component_id) REFERENCES packages_debian_group_components(id) ON DELETE RESTRICT;
ALTER TABLE ONLY incident_management_timeline_event_tags
ADD CONSTRAINT fk_rails_dd5c91484e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_callouts
ADD CONSTRAINT fk_rails_ddfdd80f3d FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

View File

@ -21,7 +21,7 @@ module API
expose :project do
expose :ci_job_token_scope_enabled do |job|
job.project.ci_job_token_scope_enabled?
job.project.ci_outbound_job_token_scope_enabled?
end
end
end

View File

@ -104,7 +104,7 @@ module API
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :ci_default_git_depth
expose :ci_forward_deployment_enabled
expose :ci_job_token_scope_enabled
expose(:ci_job_token_scope_enabled) { |p, _| p.ci_outbound_job_token_scope_enabled? }
expose :ci_separated_caches
expose :ci_opt_in_jwt
expose :ci_allow_fork_pipelines_to_run_in_parent_project

View File

@ -20,8 +20,6 @@ module API
use :pagination
use :event_filter_params
use :sort_params
optional :scope, type: String, desc: 'Include all events across a user\'s projects',
documentation: { example: 'all' }
end
get do

View File

@ -6,7 +6,7 @@ module API
extend Grape::API::Helpers
params :event_filter_params do
optional :action, type: String, values: Event.actions.keys, desc: 'Event action to filter on'
optional :action, type: String, values: Event.actions, desc: 'Event action to filter on'
optional :target_type, type: String, values: Event.target_types, desc: 'Event target type to filter on'
optional :before, type: Date, desc: 'Include only events created before this date'
optional :after, type: Date, desc: 'Include only events created after this date'

View File

@ -264,6 +264,8 @@ incident_management_oncall_shifts: :gitlab_main
incident_management_pending_alert_escalations: :gitlab_main
incident_management_pending_issue_escalations: :gitlab_main
incident_management_timeline_events: :gitlab_main
incident_management_timeline_event_tags: :gitlab_main
incident_management_timeline_event_tag_links: :gitlab_main
index_statuses: :gitlab_main
in_product_marketing_emails: :gitlab_main
insights: :gitlab_main

View File

@ -34,6 +34,7 @@ module Gitlab
alias_method :parse!, :parse
alias_method :load, :parse
alias_method :decode, :parse
# Restricted method for converting a Ruby object to JSON. If you
# need to pass options to this, you should use `.generate` instead,
@ -56,6 +57,8 @@ module Gitlab
adapter_generate(object, opts)
end
alias_method :encode, :generate
# Generates JSON for an object and makes it look purdy
#
# The Oj variant in this looks seriously weird but these are the settings

View File

@ -16,7 +16,7 @@ gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry'
gem 'rspec_junit_formatter', '~> 0.6.0'
gem 'faker', '~> 2.23'
gem 'knapsack', '~> 4.0'
gem 'parallel_tests', '~> 2.32'
gem 'parallel_tests', '~> 3.13'
gem 'rotp', '~> 6.2.0'
gem 'timecop', '~> 0.9.5'
gem 'parallel', '~> 1.19'

View File

@ -185,7 +185,7 @@ GEM
oj (3.13.21)
os (1.1.4)
parallel (1.19.2)
parallel_tests (2.32.0)
parallel_tests (3.13.0)
parallel
parser (3.1.2.1)
ast (~> 2.4.1)
@ -316,7 +316,7 @@ DEPENDENCIES
nokogiri (~> 1.13, >= 1.13.8)
octokit (~> 5.6.1)
parallel (~> 1.19)
parallel_tests (~> 2.32)
parallel_tests (~> 3.13)
pry-byebug (~> 3.5.1)
rainbow (~> 3.0.0)
rake (~> 13)

View File

@ -11,7 +11,7 @@ end
desc "Initialize GitLab with an access token"
task :initialize_gitlab_auth, [:address] do |_, args|
QA::Tools::InitializeGitLabAuth.new(args).run
QA::Tools::InitializeGitlabAuth.new(args).run
end
desc "Generate Performance Testdata"
@ -46,7 +46,7 @@ task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_l
desc "Deletes test ssh keys a user"
task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |_, args|
QA::Tools::DeleteTestSSHKeys.new(args).run
QA::Tools::DeleteTestSshKeys.new(args).run
end
desc "Deletes projects directly under the provided group"

View File

@ -30,6 +30,22 @@ module QA
loader.ignore("#{root}/specs/features")
loader.ignore("#{root}/specs/spec_helper.rb")
# we need to eager load scenario classes
# zeitwerk does not have option to configure what to eager load, so all exceptions have to be defined
loader.do_not_eager_load("#{root}/ce")
loader.do_not_eager_load("#{root}/ee")
loader.do_not_eager_load("#{root}/flow")
loader.do_not_eager_load("#{root}/git")
loader.do_not_eager_load("#{root}/mobile")
loader.do_not_eager_load("#{root}/page")
loader.do_not_eager_load("#{root}/resource")
loader.do_not_eager_load("#{root}/runtime")
loader.do_not_eager_load("#{root}/service")
loader.do_not_eager_load("#{root}/specs")
loader.do_not_eager_load("#{root}/support")
loader.do_not_eager_load("#{root}/tools")
loader.do_not_eager_load("#{root}/vendor")
loader.inflector.inflect(
"ce" => "CE",
"ee" => "EE",
@ -74,6 +90,7 @@ module QA
end
loader.setup
loader.eager_load
end
# Custom warning processing

View File

@ -1,36 +0,0 @@
# frozen_string_literal: true
module QA
module Scenario
module Test
# This class exists for back-compatibility so that gitlab-qa can continue
# to call Test::Instance instead of Test::Instance::All until at least
# the current latest GitLab version has the Test::Instance::All class.
# As of Aug, 22nd 2018. Only GitLab >= 11.3 has this class.
module Instance
include Bootable
def self.perform(*args)
self.tap do |scenario|
yield scenario if block_given?
break scenario.do_perform(*args)
end
end
def self.do_perform(address, *rspec_options)
Runtime::Scenario.define(:gitlab_address, address)
##
# Perform before hooks, which are different for CE and EE
#
Runtime::Release.perform_before_hooks
Specs::Runner.perform do |specs|
specs.tty = true
specs.options = rspec_options if rspec_options.any?
end
end
end
end
end
end

View File

@ -1,20 +0,0 @@
# frozen_string_literal: true
module QA
module Scenario
module Test
module Integration
class Github < Test::Instance::All
tags :github
def perform(address, *rspec_options)
# This test suite requires a GitHub personal access token
Runtime::Env.require_github_access_token!
super
end
end
end
end
end
end

View File

@ -5,20 +5,24 @@ require 'deprecation_toolkit/rspec'
require 'concurrent/utility/monotonic_time'
require 'active_support/gem_version'
module QaDeprecationToolkitEnv
# Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
# rubocop:disable Layout/LineLength
def self.kwargs_warning
%r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z}
end
# rubocop:enable Layout/LineLength
module QA
module Specs
class QaDeprecationToolkitEnv
# Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18
# rubocop:disable Layout/LineLength
def self.kwargs_warning
%r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z}
end
# rubocop:enable Layout/LineLength
def self.configure!
# Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7
Warning[:deprecated] = true
def self.configure!
# Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7
Warning[:deprecated] = true
DeprecationToolkit::Configuration.test_runner = :rspec
DeprecationToolkit::Configuration.deprecation_path = 'deprecations'
DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning]
DeprecationToolkit::Configuration.test_runner = :rspec
DeprecationToolkit::Configuration.deprecation_path = 'deprecations'
DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning]
end
end
end
end

View File

@ -2,8 +2,7 @@
require_relative '../../qa'
require_relative 'qa_deprecation_toolkit_env'
QaDeprecationToolkitEnv.configure!
QA::Specs::QaDeprecationToolkitEnv.configure!
Knapsack::Adapters::RSpecAdapter.bind if QA::Runtime::Env.knapsack?

View File

@ -10,42 +10,11 @@ module QA
class NonEmptySuites
include Helpers
# rubocop:disable Layout/LineLength
SCENARIOS = [
{ klass: "Test::Instance::All" },
{ klass: "Test::Instance::Smoke" },
{ klass: "Test::Instance::Reliable" },
{ klass: "Test::Instance::ReviewBlocking" },
{ klass: "Test::Instance::ReviewNonBlocking" },
{ klass: "Test::Instance::CloudActivation" },
{ klass: "Test::Instance::Integrations" },
{ klass: "Test::Instance::Jira" },
{ klass: "Test::Instance::LargeSetup" },
{ klass: "Test::Instance::Metrics" },
{ klass: "Test::Instance::ObjectStorage" },
{ klass: "Test::Instance::Packages" },
{ klass: "Test::Instance::RepositoryStorage" },
{ klass: "Test::Integration::ServicePingDisabled" },
{ klass: "Test::Integration::LDAPNoTLS" },
{ klass: "Test::Integration::LDAPTLS" },
{ klass: "Test::Integration::LDAPNoServer" },
{ klass: "Test::Integration::InstanceSAML" },
{ klass: "Test::Integration::RegistryWithCDN" },
{ klass: "Test::Integration::RegistryTLS" },
{ klass: "Test::Integration::Registry" },
{ klass: "Test::Integration::SMTP" },
{ klass: "QA::EE::Scenario::Test::Integration::Elasticsearch" },
{ klass: "QA::EE::Scenario::Test::Integration::GroupSAML" },
{
klass: "QA::EE::Scenario::Test::Geo",
args: "--primary-address http://dummy1.test --primary-name gitlab-primary --secondary-address http://dummy2.test --secondary-name gitlab-secondary --without-setup"
},
{
klass: "Test::Integration::Mattermost",
args: "--mattermost-address http://mattermost.test"
}
# @return [Array] scenarios that never run in package-and-test pipeline
IGNORED_SCENARIOS = [
"QA::EE::Scenario::Test::Geo",
"QA::Scenario::Test::Instance::Airgapped"
].freeze
# rubocop:enable Layout/LineLength
def initialize(qa_tests)
@qa_tests = qa_tests
@ -56,38 +25,58 @@ module QA
# @return [String]
def fetch
logger.info("Checking for runnable suites")
scenarios = SCENARIOS.each_with_object([]) do |scenario, runnable_scenarios|
logger.info(" fetching runnable specs for '#{scenario[:klass]}'")
scenarios.each_with_object([]) do |scenario, runnable_scenarios|
logger.info(" fetching runnable specs for '#{scenario}'")
next logger.info(" scenario is in ignore list, skipping") if IGNORED_SCENARIOS.include?(scenario)
out, err, status = run_command(**scenario)
out, err, status = run_command(scenario)
unless status.success?
logger.error(" example count failed!\n#{err}")
logger.error(" example count failed!\n#{err}")
next
end
count = out.split("\n").last.to_i
logger.info(" found #{count} examples to run")
runnable_scenarios << scenario[:klass] if count > 0
end
scenarios.join(",")
runnable_scenarios << scenario if count > 0
end.join(",")
end
private
attr_reader :qa_tests
# Get all defined scenarios
#
# @return [Array<String>]
def scenarios
foss_scenarios = scenario_classes(QA::Scenario::Test)
return foss_scenarios unless QA.const_defined?("QA::EE")
foss_scenarios + scenario_classes(QA::EE::Scenario::Test)
end
# Fetch scenario classes recursively
#
# @param [Module] mod
# @return [Array<String>]
def scenario_classes(mod)
mod.constants.map do |const|
c = mod.const_get(const, false)
next c.to_s if c.is_a?(Class)
scenario_classes(c)
end.flatten
end
# Run scenario count command
#
# @param [String] klass
# @param [String] args
# @return [String]
def run_command(klass:, args: nil)
def run_command(klass)
cmd = ["bundle exec bin/qa"]
cmd << klass
cmd << "--count-examples-only --address http://dummy1.test"
cmd << args if args
cmd << "-- #{qa_tests}" unless qa_tests.blank?
Open3.capture3(cmd.join(" "))

View File

@ -12,7 +12,7 @@
module QA
module Tools
class DeleteTestSSHKeys
class DeleteTestSshKeys
include Support::API
ITEMS_PER_PAGE = '100'

View File

@ -6,7 +6,7 @@ module QA
# Also creates a personal access token
# @example
# $ bundle exec rake 'initialize_gitlab_auth[http://gitlab.test]'
class InitializeGitLabAuth
class InitializeGitlabAuth
attr_reader :address
def initialize(address:)

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
RSpec.describe QA::Scenario::Test::Integration::Github do
describe '#perform' do
let(:env) { spy('Runtime::Env', knapsack?: false, dry_run: false) }
before do
stub_const('QA::Runtime::Env', env)
end
it_behaves_like 'a QA scenario class' do
let(:tags) { [:github] }
it 'requires a GitHub access token' do
subject.perform(args)
expect(env).to have_received(:require_github_access_token!)
end
end
end
end

View File

@ -9,11 +9,11 @@ RSpec.describe QA::Tools::Ci::NonEmptySuites do
allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new))
allow(Open3).to receive(:capture3).and_return(["output\n0", "", status])
allow(Open3).to receive(:capture3)
.with("bundle exec bin/qa Test::Instance::All --count-examples-only --address http://dummy1.test")
.with("bundle exec bin/qa QA::Scenario::Test::Instance::All --count-examples-only --address http://dummy1.test")
.and_return(["output\n1", "", status])
end
it "returns runnable test suites" do
expect(non_empty_suites.fetch).to eq("Test::Instance::All")
expect(non_empty_suites.fetch).to eq("QA::Scenario::Test::Instance::All")
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :incident_management_timeline_event_tag_link, class: 'IncidentManagement::TimelineEventTagLink' do
association :timeline_event_tag, factory: :incident_management_timeline_event_tag
association :timeline_event, factory: :incident_management_timeline_event
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :incident_management_timeline_event_tag, class: 'IncidentManagement::TimelineEventTag' do
project
name { 'Start time' }
end
end

View File

@ -54,7 +54,7 @@ FactoryBot.define do
import_last_error { nil }
forward_deployment_enabled { nil }
restrict_user_defined_variables { nil }
ci_job_token_scope_enabled { nil }
ci_outbound_job_token_scope_enabled { nil }
ci_inbound_job_token_scope_enabled { nil }
runner_token_expiration_interval { nil }
runner_token_expiration_interval_human_readable { nil }
@ -113,7 +113,7 @@ FactoryBot.define do
project.merge_trains_enabled = evaluator.merge_trains_enabled unless evaluator.merge_trains_enabled.nil?
project.keep_latest_artifact = evaluator.keep_latest_artifact unless evaluator.keep_latest_artifact.nil?
project.restrict_user_defined_variables = evaluator.restrict_user_defined_variables unless evaluator.restrict_user_defined_variables.nil?
project.ci_job_token_scope_enabled = evaluator.ci_job_token_scope_enabled unless evaluator.ci_job_token_scope_enabled.nil?
project.ci_outbound_job_token_scope_enabled = evaluator.ci_outbound_job_token_scope_enabled unless evaluator.ci_outbound_job_token_scope_enabled.nil?
project.ci_inbound_job_token_scope_enabled = evaluator.ci_inbound_job_token_scope_enabled unless evaluator.ci_inbound_job_token_scope_enabled.nil?
project.runner_token_expiration_interval = evaluator.runner_token_expiration_interval unless evaluator.runner_token_expiration_interval.nil?
project.runner_token_expiration_interval_human_readable = evaluator.runner_token_expiration_interval_human_readable unless evaluator.runner_token_expiration_interval_human_readable.nil?

View File

@ -152,6 +152,30 @@ describe('diffs/components/app', () => {
});
});
describe('fetch diff with no changes', () => {
beforeEach(() => {
const fetchResolver = () => {
store.state.diffs.retrievingBatches = false;
return Promise.resolve({ real_size: null });
};
createComponent();
jest.spyOn(wrapper.vm, 'fetchDiffFilesMeta').mockImplementation(fetchResolver);
return nextTick();
});
it('diff counter to be 0 after fetch', async () => {
expect(wrapper.vm.diffFilesLength).toEqual(0);
wrapper.vm.fetchData(false);
await nextTick();
expect(wrapper.vm.fetchDiffFilesMeta).toHaveBeenCalled();
expect(wrapper.vm.diffFilesLength).toEqual(0);
});
});
describe('codequality diff', () => {
it('does not fetch code quality data on FOSS', async () => {
createComponent();

View File

@ -141,6 +141,14 @@ describe('OverviewTabs', () => {
expect(tabPanel.vm.$attrs.lazy).toBe(false);
});
it('sets `lazy` prop to `false` for initially active tab and `true` for all other tabs', async () => {
await createComponent({ route: { name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } } });
expect(findTabPanels().at(0).vm.$attrs.lazy).toBe(true);
expect(findTabPanels().at(1).vm.$attrs.lazy).toBe(false);
expect(findTabPanels().at(2).vm.$attrs.lazy).toBe(true);
});
describe.each([
[
{ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },

View File

@ -1,6 +1,6 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mockTracking } from 'helpers/tracking_helper';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -27,7 +27,7 @@ describe('GitlabVersionCheck', () => {
mock = new MockAdapter(axios);
mock.onGet().replyOnce(response.code, response.res);
wrapper = shallowMount(GitlabVersionCheck);
wrapper = shallowMountExtended(GitlabVersionCheck);
};
const dummyGon = {
@ -42,6 +42,7 @@ describe('GitlabVersionCheck', () => {
window.gon = originalGon;
});
const findGlBadgeClickWrapper = () => wrapper.findByTestId('badge-click-wrapper');
const findGlBadge = () => wrapper.findComponent(GlBadge);
describe.each`
@ -81,7 +82,8 @@ describe('GitlabVersionCheck', () => {
await waitForPromises(); // Ensure we wrap up the axios call
});
it(`does${renders ? '' : ' not'} render GlBadge`, () => {
it(`does${renders ? '' : ' not'} render Badge Click Wrapper and GlBadge`, () => {
expect(findGlBadgeClickWrapper().exists()).toBe(renders);
expect(findGlBadge().exists()).toBe(renders);
});
});
@ -110,9 +112,9 @@ describe('GitlabVersionCheck', () => {
expect(findGlBadge().attributes('variant')).toBe(expectedUI.variant);
});
it(`tracks rendered_version_badge with status ${expectedUI.variant}`, () => {
it(`tracks rendered_version_badge with label ${expectedUI.title}`, () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'rendered_version_badge', {
label: expectedUI.variant,
label: expectedUI.title,
});
});
@ -120,11 +122,11 @@ describe('GitlabVersionCheck', () => {
expect(findGlBadge().attributes('href')).toBe(UPGRADE_DOCS_URL);
});
it(`tracks click_version_badge with status ${expectedUI.variant} when badge is clicked`, async () => {
await findGlBadge().vm.$emit('click');
it(`tracks click_version_badge with label ${expectedUI.title} when badge is clicked`, async () => {
await findGlBadgeClickWrapper().trigger('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_version_badge', {
label: expectedUI.variant,
label: expectedUI.title,
});
});
});

View File

@ -8,7 +8,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject do
describe '#resolve' do
let_it_be(:project) do
create(:project, ci_job_token_scope_enabled: true).tap(&:save!)
create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!)
end
let_it_be(:target_project) { create(:project) }

View File

@ -7,7 +7,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::RemoveProject do
end
describe '#resolve' do
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:target_project) { create(:project) }
let_it_be(:link) do

View File

@ -6,7 +6,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
specify do
expect(described_class).to have_nullable_graphql_type(::Types::Ci::JobTokenScopeType)
@ -21,7 +21,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do
end
it 'returns the same project in the allow list of projects for the Ci Job Token when scope is not enabled' do
allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
allow(project).to receive(:ci_outbound_job_token_scope_enabled?).and_return(false)
expect(resolve_scope.all_projects).to contain_exactly(project)
end
@ -40,7 +40,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do
context 'when job token scope is disabled' do
before do
project.update!(ci_job_token_scope_enabled: false)
project.update!(ci_outbound_job_token_scope_enabled: false)
end
it 'resolves projects' do

View File

@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiJobTokenScopeType'] do
end
describe 'query' do
let(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:current_user) { create(:user) }
let(:query) do

View File

@ -61,6 +61,8 @@ issues:
- requirement
- incident_management_issuable_escalation_status
- incident_management_timeline_events
- incident_management_timeline_event_tags
- incident_management_timeline_event_links
- pending_escalations
- customer_relations_contacts
- issue_customer_relations_contacts
@ -622,6 +624,7 @@ project:
- incident_management_oncall_rotations
- incident_management_escalation_policies
- incident_management_issuable_escalation_statuses
- incident_management_timeline_event_tags
- debian_distributions
- merge_request_metrics
- security_orchestration_policy_configuration

View File

@ -8,6 +8,12 @@ RSpec.describe Gitlab::Json do
end
describe ".parse" do
it "is aliased" do
[:parse!, :load, :decode].each do |method|
expect(described_class.method(method)).to eq(described_class.method(:parse))
end
end
context "legacy_mode is disabled by default" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
@ -178,6 +184,10 @@ RSpec.describe Gitlab::Json do
{ test: true, "foo.bar" => "baz", is_json: 1, some: [1, 2, 3] }
end
it "is aliased" do
expect(described_class.method(:encode)).to eq(described_class.method(:generate))
end
it "generates JSON" do
expected_string = <<~STR.chomp
{"test":true,"foo.bar":"baz","is_json":1,"some":[1,2,3]}

View File

@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::JobToken::Scope do
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let(:scope) { described_class.new(project) }
@ -53,7 +53,7 @@ RSpec.describe Ci::JobToken::Scope do
context 'when project scope setting is disabled' do
before do
project.ci_job_token_scope_enabled = false
project.ci_outbound_job_token_scope_enabled = false
end
it 'considers any project to be part of the scope' do

View File

@ -13,6 +13,12 @@ RSpec.describe IncidentManagement::TimelineEvent do
it { is_expected.to belong_to(:incident) }
it { is_expected.to belong_to(:updated_by_user) }
it { is_expected.to belong_to(:promoted_from_note) }
it { is_expected.to have_many(:timeline_event_tag_links).class_name('IncidentManagement::TimelineEventTagLink') }
it do
is_expected.to have_many(:timeline_event_tags)
.class_name('IncidentManagement::TimelineEventTag').through(:timeline_event_tag_links)
end
end
describe 'validations' do

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::TimelineEventTagLink do
describe 'associations' do
it { is_expected.to belong_to(:timeline_event) }
it { is_expected.to belong_to(:timeline_event_tag) }
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::TimelineEventTag do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:timeline_event_tag_links).class_name('IncidentManagement::TimelineEventTagLink') }
it {
is_expected.to have_many(:timeline_events)
.class_name('IncidentManagement::TimelineEvent').through(:timeline_event_tag_links)
}
end
describe 'validations' do
subject { build(:incident_management_timeline_event_tag) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to([:project_id]) }
it { is_expected.to allow_value('Test tag 1').for(:name) }
it { is_expected.not_to allow_value('Test tag, 1').for(:name) }
it { is_expected.not_to allow_value('').for(:name) }
it { is_expected.not_to allow_value('s' * 256).for(:name) }
end
end

View File

@ -149,6 +149,7 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_one(:build_artifacts_size_refresh).class_name('Projects::BuildArtifactsSizeRefresh') }
it { is_expected.to have_many(:project_callouts).class_name('Users::ProjectCallout').with_foreign_key(:project_id) }
it { is_expected.to have_many(:pipeline_metadata).class_name('Ci::PipelineMetadata') }
it { is_expected.to have_many(:incident_management_timeline_event_tags).class_name('IncidentManagement::TimelineEventTag') }
# GitLab Pages
it { is_expected.to have_many(:pages_domains) }
@ -845,6 +846,8 @@ RSpec.describe Project, factory_default: :keep do
end
describe 'delegation' do
let_it_be(:project) { create(:project) }
[:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_member, :add_members].each do |method|
it { is_expected.to delegate_method(method).to(:team) }
end
@ -887,8 +890,24 @@ RSpec.describe Project, factory_default: :keep do
end
include_examples 'ci_cd_settings delegation' do
# Skip attributes defined in EE code
let(:attributes_with_prefix) do
{
'group_runners_enabled' => '',
'default_git_depth' => 'ci_',
'forward_deployment_enabled' => 'ci_',
'keep_latest_artifact' => '',
'restrict_user_defined_variables' => '',
'runner_token_expiration_interval' => '',
'separated_caches' => 'ci_',
'opt_in_jwt' => 'ci_',
'allow_fork_pipelines_to_run_in_parent_project' => 'ci_',
'inbound_job_token_scope_enabled' => 'ci_',
'job_token_scope_enabled' => 'ci_outbound_'
}
end
let(:exclude_attributes) do
# Skip attributes defined in EE code
%w(
merge_pipelines_enabled
merge_trains_enabled
@ -909,8 +928,8 @@ RSpec.describe Project, factory_default: :keep do
end
end
describe '#ci_job_token_scope_enabled?' do
it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do
describe '#ci_outbound_job_token_scope_enabled?' do
it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_outbound_' do
let(:delegated_method) { :job_token_scope_enabled? }
end
end

View File

@ -2437,7 +2437,7 @@ RSpec.describe ProjectPolicy do
before do
current_user.set_ci_job_token_scope!(job)
current_user.external = external_user
scope_project.update!(ci_job_token_scope_enabled: token_scope_enabled)
scope_project.update!(ci_outbound_job_token_scope_enabled: token_scope_enabled)
end
it "enforces the expected permissions" do

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'JobArtifactsDestroy' do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:job) { create(:ci_build) }
let(:mutation) do
variables = {
id: job.to_global_id.to_s
}
graphql_mutation(:job_artifacts_destroy, variables, <<~FIELDS)
job {
name
}
destroyedArtifactsCount
errors
FIELDS
end
before do
create(:ci_job_artifact, :archive, job: job)
create(:ci_job_artifact, :junit, job: job)
end
context 'when the user is not allowed to destroy the job artifacts' do
it 'returns an error' do
post_graphql_mutation(mutation, current_user: user)
expect(graphql_errors).not_to be_empty
expect(job.reload.job_artifacts.count).to be(2)
end
end
context 'when the user is allowed to destroy the job artifacts' do
before do
job.project.add_maintainer(user)
end
it 'destroys the job artifacts and returns the expected data' do
expected_data = {
'jobArtifactsDestroy' => {
'errors' => [],
'destroyedArtifactsCount' => 2,
'job' => {
'name' => job.name
}
}
}
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
expect(graphql_data).to eq(expected_data)
expect(job.reload.job_artifacts.count).to be(0)
end
context 'when the the project this job belongs to is undergoing stats refresh' do
it 'destroys no artifacts and returns the correct error' do
allow_next_found_instance_of(Project) do |project|
allow(project).to receive(:refreshing_build_artifacts_size?).and_return(true)
end
expected_data = {
'jobArtifactsDestroy' => {
'errors' => ['Action temporarily disabled. The project this job belongs to is undergoing stats refresh.'],
'destroyedArtifactsCount' => 0,
'job' => {
'name' => job.name
}
}
}
post_graphql_mutation(mutation, current_user: user)
expect(response).to have_gitlab_http_status(:success)
expect(graphql_data).to eq(expected_data)
expect(job.reload.job_artifacts.count).to be(2)
end
end
end
end

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'CiJobTokenScopeAddProject' do
include GraphqlHelpers
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:target_project) { create(:project) }
let(:variables) do

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'CiJobTokenScopeRemoveProject' do
include GraphqlHelpers
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:target_project) { create(:project) }
let_it_be(:link) do

View File

@ -8,7 +8,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do
let_it_be(:project) do
create(:project,
keep_latest_artifact: true,
ci_job_token_scope_enabled: true,
ci_outbound_job_token_scope_enabled: true,
ci_inbound_job_token_scope_enabled: true
).tap(&:save!)
end
@ -66,7 +66,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do
project.reload
expect(response).to have_gitlab_http_status(:success)
expect(project.ci_job_token_scope_enabled).to eq(false)
expect(project.ci_outbound_job_token_scope_enabled).to eq(false)
end
it 'does not update job_token_scope_enabled if not specified' do
@ -77,7 +77,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do
project.reload
expect(response).to have_gitlab_http_status(:success)
expect(project.ci_job_token_scope_enabled).to eq(true)
expect(project.ci_outbound_job_token_scope_enabled).to eq(true)
end
describe 'inbound_job_token_scope_enabled' do

View File

@ -14,6 +14,7 @@ RSpec.describe Ci::JobArtifacts::DeleteService do
result = service.execute
expect(result).to be_success
expect(result[:destroyed_artifacts_count]).to be(2)
end
it 'deletes erasable artifacts' do
@ -24,7 +25,7 @@ RSpec.describe Ci::JobArtifacts::DeleteService do
expect { service.execute }.not_to change { build.has_trace? }.from(true)
end
context 'when project is undergoing statistics refresh' do
context 'when project is undergoing stats refresh' do
before do
allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true)
end
@ -36,6 +37,30 @@ RSpec.describe Ci::JobArtifacts::DeleteService do
service.execute
end
it 'returns an error response with the correct message and reason' do
result = service.execute
expect(result).to be_error
expect(result[:message]).to be('Action temporarily disabled. ' \
'The project this job belongs to is undergoing stats refresh.')
expect(result[:reason]).to be(:project_stats_refresh)
end
end
context 'when an error response is received from DestroyBatchService' do
before do
allow_next_instance_of(Ci::JobArtifacts::DestroyBatchService) do |service|
allow(service).to receive(:execute).and_return({ status: :error, message: 'something went wrong' })
end
end
it 'returns an error response with the correct message' do
result = service.execute
expect(result).to be_error
expect(result[:message]).to be('something went wrong')
end
end
end
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Ci::JobTokenScope::AddProjectService do
let(:service) { described_class.new(project, current_user) }
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:target_project) { create(:project) }
let_it_be(:current_user) { create(:user) }

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Ci::JobTokenScope::RemoveProjectService do
let(:service) { described_class.new(project, current_user) }
let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) }
let_it_be(:target_project) { create(:project) }
let_it_be(:current_user) { create(:user) }

View File

@ -5,12 +5,14 @@ RSpec.shared_examples 'ci_cd_settings delegation' do
context 'when ci_cd_settings is destroyed but project is not' do
it 'allows methods delegated to ci_cd_settings to be nil', :aggregate_failures do
project = create(:project)
attributes = project.ci_cd_settings.attributes.keys - %w(id project_id) - exclude_attributes
expect(attributes).to match_array(attributes_with_prefix.keys)
project.ci_cd_settings.destroy!
project.reload
attributes.each do |attr|
method = project.respond_to?("ci_#{attr}") ? "ci_#{attr}" : attr
attributes_with_prefix.each do |attr, prefix|
method = "#{prefix}#{attr}"
expect(project.send(method)).to be_nil, "#{attr} was not nil"
end
end
@ -20,8 +22,6 @@ end
RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project) }
context 'when ci_cd_settings is nil' do
before do
allow(project).to receive(:ci_cd_settings).and_return(nil)