Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-11-21 09:32:23 +00:00
parent fdca4d890d
commit 90d6654d6e
48 changed files with 1558 additions and 1182 deletions

View File

@ -1293,7 +1293,6 @@ RSpec/BeEq:
- 'spec/requests/api/settings_spec.rb'
- 'spec/requests/api/users_preferences_spec.rb'
- 'spec/requests/api/users_spec.rb'
- 'spec/requests/api/virtual_registries/packages/maven_spec.rb'
- 'spec/requests/groups/deploy_tokens_controller_spec.rb'
- 'spec/requests/openid_connect_spec.rb'
- 'spec/requests/organizations/organizations_controller_spec.rb'

View File

@ -444,7 +444,6 @@ RSpec/ReceiveMessages:
- 'spec/requests/api/helpers_spec.rb'
- 'spec/requests/api/maven_packages_spec.rb'
- 'spec/requests/api/search_spec.rb'
- 'spec/requests/api/virtual_registries/packages/maven_spec.rb'
- 'spec/requests/projects/google_cloud/databases_controller_spec.rb'
- 'spec/requests/projects/google_cloud/service_accounts_controller_spec.rb'
- 'spec/requests/projects/redirect_controller_spec.rb'

View File

@ -1 +1 @@
ccbba5a4c00e02b85995b6ad75ed5fa3274ccaea
f519c72b578be1182b4492a958c1fc48fe593e76

View File

@ -464,7 +464,7 @@
{"name":"opentelemetry-exporter-otlp","version":"0.29.0","platform":"ruby","checksum":"4d1ca0e01cab8127dcc43eb3b51845dfce2b972d51033df7ce33adbdc30681fa"},
{"name":"opentelemetry-helpers-sql-obfuscation","version":"0.1.0","platform":"ruby","checksum":"bc6ef1373dbcf979647091b3bfc99d7b6fb9669f74c3ae184f58b48adfc8d432"},
{"name":"opentelemetry-instrumentation-action_mailer","version":"0.2.0","platform":"ruby","checksum":"88f2dd8cff27886e84bbf522b698f0bf86b83f0f0adb0d3d27b3fa8211b1cb0e"},
{"name":"opentelemetry-instrumentation-action_pack","version":"0.9.0","platform":"ruby","checksum":"c5df8472afc9cdbfc1425d9af7816b9cfc1a1a69b86621f1fc624974bd9acb9a"},
{"name":"opentelemetry-instrumentation-action_pack","version":"0.10.0","platform":"ruby","checksum":"2d821a45be4c2a281cfa42ec96562268b50ff5d5fac77386e7e39e792bceab02"},
{"name":"opentelemetry-instrumentation-action_view","version":"0.7.3","platform":"ruby","checksum":"6da829154e751bd88f5369b97a6346e12c3583a784f58178ccaa0b46d301ea21"},
{"name":"opentelemetry-instrumentation-active_job","version":"0.7.8","platform":"ruby","checksum":"281b3d9bace7aac9f1e121b75f294e7ee8beaedd1f20405539c54df2e6763942"},
{"name":"opentelemetry-instrumentation-active_record","version":"0.8.0","platform":"ruby","checksum":"199d83102e81f5d6236025f4e6507513ffd2e6a2231e9951d0723708cc96c1c8"},
@ -482,7 +482,7 @@
{"name":"opentelemetry-instrumentation-net_http","version":"0.22.7","platform":"ruby","checksum":"c07427ff6b7bed124bf004008be4d3a4aef8865629f7a2c4614c4a8d357246d0"},
{"name":"opentelemetry-instrumentation-pg","version":"0.29.0","platform":"ruby","checksum":"6fcbdddfb757fed97b3bce0fb9f5f206d4cdb5c24e3da78c0dc54100c195f3d1"},
{"name":"opentelemetry-instrumentation-rack","version":"0.25.0","platform":"ruby","checksum":"539c8b4f6b818e16495e9016126537b4d1cc53292219f1acc35006fe30ce243d"},
{"name":"opentelemetry-instrumentation-rails","version":"0.32.0","platform":"ruby","checksum":"0851ca626d608dc1f6bcd12d81d65a61fd0299f2ccd23cc9bf444309a831d28e"},
{"name":"opentelemetry-instrumentation-rails","version":"0.33.0","platform":"ruby","checksum":"f00cc90ffeb9d612225b2797162efafcf60eda032330e138d4493bd66310206d"},
{"name":"opentelemetry-instrumentation-rake","version":"0.2.2","platform":"ruby","checksum":"fbde8a6aab77c09bf0f94d914dd26dcf2e23ec67e2300f06a1cb8294a97d8020"},
{"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"},
{"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"},

View File

@ -1291,7 +1291,7 @@ GEM
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-action_pack (0.9.0)
opentelemetry-instrumentation-action_pack (0.10.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21)
@ -1349,10 +1349,10 @@ GEM
opentelemetry-instrumentation-rack (0.25.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.32.0)
opentelemetry-instrumentation-rails (0.33.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.2.0)
opentelemetry-instrumentation-action_pack (~> 0.9.0)
opentelemetry-instrumentation-action_pack (~> 0.10.0)
opentelemetry-instrumentation-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.8.0)

View File

@ -469,7 +469,7 @@
{"name":"opentelemetry-exporter-otlp","version":"0.29.0","platform":"ruby","checksum":"4d1ca0e01cab8127dcc43eb3b51845dfce2b972d51033df7ce33adbdc30681fa"},
{"name":"opentelemetry-helpers-sql-obfuscation","version":"0.1.0","platform":"ruby","checksum":"bc6ef1373dbcf979647091b3bfc99d7b6fb9669f74c3ae184f58b48adfc8d432"},
{"name":"opentelemetry-instrumentation-action_mailer","version":"0.2.0","platform":"ruby","checksum":"88f2dd8cff27886e84bbf522b698f0bf86b83f0f0adb0d3d27b3fa8211b1cb0e"},
{"name":"opentelemetry-instrumentation-action_pack","version":"0.9.0","platform":"ruby","checksum":"c5df8472afc9cdbfc1425d9af7816b9cfc1a1a69b86621f1fc624974bd9acb9a"},
{"name":"opentelemetry-instrumentation-action_pack","version":"0.10.0","platform":"ruby","checksum":"2d821a45be4c2a281cfa42ec96562268b50ff5d5fac77386e7e39e792bceab02"},
{"name":"opentelemetry-instrumentation-action_view","version":"0.7.3","platform":"ruby","checksum":"6da829154e751bd88f5369b97a6346e12c3583a784f58178ccaa0b46d301ea21"},
{"name":"opentelemetry-instrumentation-active_job","version":"0.7.8","platform":"ruby","checksum":"281b3d9bace7aac9f1e121b75f294e7ee8beaedd1f20405539c54df2e6763942"},
{"name":"opentelemetry-instrumentation-active_record","version":"0.8.0","platform":"ruby","checksum":"199d83102e81f5d6236025f4e6507513ffd2e6a2231e9951d0723708cc96c1c8"},
@ -487,7 +487,7 @@
{"name":"opentelemetry-instrumentation-net_http","version":"0.22.7","platform":"ruby","checksum":"c07427ff6b7bed124bf004008be4d3a4aef8865629f7a2c4614c4a8d357246d0"},
{"name":"opentelemetry-instrumentation-pg","version":"0.29.0","platform":"ruby","checksum":"6fcbdddfb757fed97b3bce0fb9f5f206d4cdb5c24e3da78c0dc54100c195f3d1"},
{"name":"opentelemetry-instrumentation-rack","version":"0.25.0","platform":"ruby","checksum":"539c8b4f6b818e16495e9016126537b4d1cc53292219f1acc35006fe30ce243d"},
{"name":"opentelemetry-instrumentation-rails","version":"0.32.0","platform":"ruby","checksum":"0851ca626d608dc1f6bcd12d81d65a61fd0299f2ccd23cc9bf444309a831d28e"},
{"name":"opentelemetry-instrumentation-rails","version":"0.33.0","platform":"ruby","checksum":"f00cc90ffeb9d612225b2797162efafcf60eda032330e138d4493bd66310206d"},
{"name":"opentelemetry-instrumentation-rake","version":"0.2.2","platform":"ruby","checksum":"fbde8a6aab77c09bf0f94d914dd26dcf2e23ec67e2300f06a1cb8294a97d8020"},
{"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"},
{"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"},

View File

@ -1306,7 +1306,7 @@ GEM
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-action_pack (0.9.0)
opentelemetry-instrumentation-action_pack (0.10.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21)
@ -1364,10 +1364,10 @@ GEM
opentelemetry-instrumentation-rack (0.25.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.32.0)
opentelemetry-instrumentation-rails (0.33.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.2.0)
opentelemetry-instrumentation-action_pack (~> 0.9.0)
opentelemetry-instrumentation-action_pack (~> 0.10.0)
opentelemetry-instrumentation-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.8.0)

View File

@ -132,7 +132,6 @@ export default {
<clipboard-button
:text="cloneSshUrlDisplay"
:title="$options.i18n.copyToClipboard"
data-clipboard-text
data-clipboard-target="#clone-ssh-url"
/>
</template>
@ -151,7 +150,6 @@ export default {
<clipboard-button
:text="cloneHttpUrlDisplay"
:title="$options.i18n.copyToClipboard"
data-clipboard-text
data-clipboard-target="#clone-http-url"
/>
</template>
@ -175,7 +173,6 @@ export default {
<clipboard-button
:text="directoryCommand"
:title="$options.i18n.copyToClipboard"
data-clipboard-text
data-clipboard-target="#go-to-directory"
/>
</template>
@ -194,7 +191,6 @@ export default {
<clipboard-button
:text="installCommand"
:title="$options.i18n.copyToClipboard"
data-clipboard-text
data-clipboard-target="#install-gollum"
/>
</template>
@ -213,7 +209,6 @@ export default {
<clipboard-button
:text="gollumCommand"
:title="$options.i18n.copyToClipboard"
data-clipboard-text
data-clipboard-target="#run-gollum"
/>
</template>

View File

@ -1,6 +1,8 @@
<script>
import { GlBadge, GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui';
import { reportToSentry } from '~/ci/utils';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { InternalEvents } from '~/tracking';
import { JM_JENKINS_TITLE_ICON_NAME, JM_MIGRATION_LINK, JM_EVENT_NAME } from '../constants';
@ -25,6 +27,14 @@ export default {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
featureId: {
type: String,
required: true,
},
},
i18n: {
title: s__('mrWidget|Migrate to GitLab CI/CD from Jenkins'),
@ -36,6 +46,14 @@ export default {
},
methods: {
dismiss() {
axios
.post(this.path, {
feature_name: this.featureId,
})
.catch((error) => {
reportToSentry(this.$options.name, error);
});
this.trackEvent(this.$options.JM_EVENT_NAME);
this.$emit('dismiss');

View File

@ -194,9 +194,9 @@ export default {
return !hasCI && mergeRequestAddCiConfigPath && !isDismissedSuggestPipeline;
},
showRenderMigrateFromJenkins() {
const { hasCI, isIntegrationJenkinsDismissed, ciIntegrationJenkins } = this.mr;
const { hasCI, isDismissedJenkinsMigration, ciIntegrationJenkins } = this.mr;
return hasCI && !isIntegrationJenkinsDismissed && ciIntegrationJenkins;
return hasCI && !isDismissedJenkinsMigration && ciIntegrationJenkins;
},
shouldRenderCollaborationStatus() {
return this.mr.allowCollaboration && this.mr.isOpen;
@ -501,7 +501,7 @@ export default {
this.mr.isDismissedSuggestPipeline = true;
},
dismissMigrateFromJenkins() {
this.mr.isIntegrationJenkinsDismissed = true;
this.mr.isDismissedJenkinsMigration = true;
},
},
};
@ -526,6 +526,8 @@ export default {
v-if="showRenderMigrateFromJenkins"
class="mr-widget-workflow"
:human-access="formattedHumanAccess"
:path="mr.userCalloutsPath"
:feature-id="mr.migrateJenkinsFeatureId"
@dismiss="dismissMigrateFromJenkins"
/>
<mr-widget-pipeline-container

View File

@ -290,10 +290,11 @@ export default class MergeRequestStore {
this.sourceProjectDefaultUrl = data.source_project_default_url;
this.userCalloutsPath = data.user_callouts_path;
this.suggestPipelineFeatureId = data.suggest_pipeline_feature_id;
this.migrateJenkinsFeatureId = data.migrate_jenkins_feature_id;
this.isDismissedSuggestPipeline = data.is_dismissed_suggest_pipeline;
this.isDismissedJenkinsMigration = data.is_dismissed_jenkins_migration;
this.securityReportsDocsPath = data.security_reports_docs_path;
this.securityConfigurationPath = data.security_configuration_path;
this.isIntegrationJenkinsDismissed = false;
// code quality
const blobPath = data.blob_path || {};

View File

@ -540,7 +540,8 @@ module ApplicationSettingsHelper
:ai_action_api_rate_limit,
:code_suggestions_api_rate_limit,
:require_personal_access_token_expiry,
:observability_backend_ssl_verification_enabled
:observability_backend_ssl_verification_enabled,
:show_migrate_from_jenkins_banner
].tap do |settings|
unless Gitlab.com?
settings << :resource_usage_limits

View File

@ -302,7 +302,8 @@ module ApplicationSettingImplementation
require_personal_access_token_expiry: true,
pages_extra_deployments_default_expiry_seconds: 86400,
scan_execution_policies_action_limit: 10,
seat_control: 0
seat_control: 0,
show_migrate_from_jenkins_banner: true
}.tap do |hsh|
hsh.merge!(non_production_defaults) unless Rails.env.production?
end

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
class CustomerRelations::IssueContact < ApplicationRecord
include EachBatch
self.table_name = "issue_customer_relations_contacts"
belongs_to :issue, optional: false, inverse_of: :customer_relations_contacts

View File

@ -16,6 +16,7 @@ module Namespaces
delegate :execute_hooks, :execute_integrations, :group, to: :project, allow_nil: true
delegate :external_references_supported?, :default_issues_tracker?, :pending_delete?, to: :project
delegate :service_desk_alias_address, to: :project
delegate :crm_group, to: :project
def self.sti_name
'Project'

View File

@ -7,6 +7,7 @@ class MergeRequestWidgetEntity < Grape::Entity
include ApplicationSettingsHelper
SUGGEST_PIPELINE = 'suggest_pipeline'
MIGRATE_FROM_JENKINS_BANNER = 'migrate_from_jenkins_banner'
expose :id
expose :iid
@ -80,6 +81,10 @@ class MergeRequestWidgetEntity < Grape::Entity
SUGGEST_PIPELINE
end
expose :migrate_jenkins_feature_id do |_merge_request|
MIGRATE_FROM_JENKINS_BANNER
end
expose :is_dismissed_suggest_pipeline do |_merge_request|
next true unless current_user
next true unless Gitlab::CurrentSettings.suggest_pipeline_enabled?
@ -87,6 +92,13 @@ class MergeRequestWidgetEntity < Grape::Entity
current_user.dismissed_callout?(feature_name: SUGGEST_PIPELINE)
end
expose :is_dismissed_jenkins_migration do |_merge_request|
next true unless current_user
next true unless Gitlab::CurrentSettings.show_migrate_from_jenkins_banner?
current_user.dismissed_callout?(feature_name: MIGRATE_FROM_JENKINS_BANNER)
end
expose :human_access do |merge_request|
merge_request.project.team.human_max_access(current_user&.id)
end

View File

@ -21,6 +21,8 @@ module Packages
e,
class: self.class.name
)
ServiceResponse.error(message: "Error processing #{file_name}")
end
private

View File

@ -5,14 +5,41 @@ module WorkItems
module Widgets
class CrmContacts < Base
def after_save_commit
# copy contacts, e.g.
# return unless work_item.namespace.root_ancestor == target_work_item.namespace.root_ancestor
#
# target_work_item.customer_relations_contacts = work_item.customer_relations_contacts
return unless target_work_item.get_widget(:crm_contacts)
if work_item.namespace.crm_group == target_work_item.namespace.crm_group
work_item.issue_customer_relations_contacts.each_batch(of: BATCH_SIZE) do |issue_contacts_batch|
CustomerRelations::IssueContact.insert_all(attributes(target_work_item, issue_contacts_batch))
end
else
::SystemNoteService.change_issuable_contacts(
target_work_item,
target_work_item.namespace,
current_user,
0, # count of added contacts
work_item.customer_relations_contacts.count # count of removed contacts
)
end
end
def post_move_cleanup
# do it
work_item.issue_customer_relations_contacts.each_batch(of: BATCH_SIZE) do |contacts_batch|
contacts_batch.delete_all
end
end
private
def attributes(target_work_item, issue_contacts)
issue_contacts.map do |issue_contact|
if params[:operation] == :move
issue_contact.attributes.except("id").tap { |c| c["issue_id"] = target_work_item.id }
else
issue_contact.attributes.except("id", "created_at", "updated_at").tap do |c|
c["issue_id"] = target_work_item.id
end
end
end
end
end
end

View File

@ -68,6 +68,8 @@
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings.md', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.gitlab_ui_checkbox_component :suggest_pipeline_enabled, s_('AdminSettings|Enable pipeline suggestion banner'), help_text: s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.')
.form-group
= f.gitlab_ui_checkbox_component :show_migrate_from_jenkins_banner, s_('AdminSettings|Show the migrate from Jenkins banner'), help_text: s_('AdminSettings|Show a banner on merge requests in projects with Jenkins pipelines to prompt migration to GitLab CI/CD instead.')
#js-runner-token-expiration-intervals{ data: runner_token_expiration_interval_attributes }
.form-group
= f.gitlab_ui_checkbox_component :enable_artifact_external_redirect_warning_page, s_('AdminSettings|Enable the external redirect warning page for job artifacts'), help_text: s_('AdminSettings|Show a redirect page that warns you about user-generated content in GitLab Pages.')

View File

@ -0,0 +1,12 @@
---
api_type: boolean
attr: show_migrate_from_jenkins_banner
clusterwide: false
column: show_migrate_from_jenkins_banner
db_type: boolean
default: 'true'
description: Show the migrate from jenkins banner
encrypted: false
gitlab_com_different_than_default: false
jihu: false
not_null: true

View File

@ -4,7 +4,7 @@ action: scan
identifiers:
- project
- user
product_group: threat_insights
product_group: security_insights
milestone: '14.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166618
distributions:

View File

@ -1,7 +1,7 @@
---
description: Tracks toggling of the security training provider setting.
action: toggle_security_training_provider
product_group: threat_insights
product_group: security_insights
milestone: '14.8'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80458
distributions:

View File

@ -2,7 +2,7 @@
data_category: optional
key_path: redis_hll_counters.secure.users_expanding_secure_security_report_monthly
description: Count of expanding the security report widget
product_group: threat_insights
product_group: security_insights
value_type: number
status: active
milestone: '13.11'

View File

@ -2,7 +2,7 @@
data_category: optional
key_path: redis_hll_counters.secure.users_expanding_secure_security_report_weekly
description: Count of expanding the security report widget
product_group: threat_insights
product_group: security_insights
value_type: number
status: active
milestone: '13.11'

View File

@ -59,6 +59,7 @@
"runner",
"scalability",
"secret_detection",
"security_insights",
"security_policies",
"source_code",
"static_analysis",
@ -66,7 +67,6 @@
"switchboard",
"technical_writing",
"tenant_scale",
"threat_insights",
"utilization",
"ux_paper_cuts",
"vulnerability_research"

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddMigrateJenkinsBannerApplicationSetting < Gitlab::Database::Migration[2.2]
milestone '17.7'
def change
add_column(:application_settings, :show_migrate_from_jenkins_banner, :boolean, default: true, null: false)
end
end

View File

@ -0,0 +1 @@
1ebb03ca5056c54bc0fc9261d20473c1e15e849bffe23370ff4aa3dcbed1848f

View File

@ -6900,6 +6900,7 @@ CREATE TABLE application_settings (
integrations jsonb DEFAULT '{}'::jsonb NOT NULL,
user_seat_management jsonb DEFAULT '{}'::jsonb NOT NULL,
resource_usage_limits jsonb DEFAULT '{}'::jsonb NOT NULL,
show_migrate_from_jenkins_banner boolean DEFAULT true NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),

View File

@ -362,6 +362,21 @@ To disable the banner:
1. Clear the **Enable pipeline suggestion banner** checkbox.
1. Select **Save changes**.
## Disable the migrate from Jenkins banner
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/470025) in GitLab 17.7.
By default, a banner shows in merge requests in projects with the [Jenkins integration enabled](../../integration/jenkins.md) to prompt migration to GitLab CI/CD.
![A banner prompting migration from Jenkins to GitLab CI](img/suggest_migrate_from_jenkins_v_17_7.png)
To disable the banner:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > CI/CD**.
1. Clear the **Show the migrate from Jenkins banner** checkbox.
1. Select **Save changes**.
## Required pipeline configuration
DETAILS:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -205,6 +205,7 @@ module API
optional :floc_enabled, type: Grape::API::Boolean, desc: 'Enable FloC (Federated Learning of Cohorts)'
optional :user_deactivation_emails_enabled, type: Boolean, desc: 'Send emails to users upon account deactivation'
optional :suggest_pipeline_enabled, type: Boolean, desc: 'Enable pipeline suggestion banner'
optional :show_migrate_from_jenkins_banner, type: Boolean, desc: 'Enable Jenkins migration banner'
optional :enable_artifact_external_redirect_warning_page, type: Boolean, desc: 'Show the external redirect page that warns you about user-generated content in GitLab Pages'
optional :users_get_by_id_limit, type: Integer, desc: "Maximum number of calls to the /users/:id API per 10 minutes per user. Set to 0 for unlimited requests."
optional :runner_token_expiration_interval, type: Integer, desc: 'Token expiration interval for shared runners, in seconds'

View File

@ -4370,9 +4370,15 @@ msgstr ""
msgid "AdminSettings|Setting must be greater than 0."
msgstr ""
msgid "AdminSettings|Show a banner on merge requests in projects with Jenkins pipelines to prompt migration to GitLab CI/CD instead."
msgstr ""
msgid "AdminSettings|Show a redirect page that warns you about user-generated content in GitLab Pages."
msgstr ""
msgid "AdminSettings|Show the migrate from Jenkins banner"
msgstr ""
msgid "AdminSettings|Silent exports by admins %{silent_admin_exports_link_start}%{icon}%{silent_admin_exports_link_end}"
msgstr ""

View File

@ -17,6 +17,7 @@ class SemgrepResultProcessor
<small>
This AppSec automation is currently under testing.
Use ~"appsec-sast::helpful" or ~"appsec-sast::unhelpful" for quick feedback.
To stop the bot from further commenting, you can use the ~"appsec-sast::stop" label.
For any detailed feedback, [add a comment here](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/issues/38).
</small>
@ -30,6 +31,11 @@ class SemgrepResultProcessor
perform_allowlist_check
semgrep_results = get_sast_results
unique_results = filter_duplicate_findings(semgrep_results)
if sast_stop_label_present?
puts "Not adding comments for this MR as it has the appsec-sast::stop label. Here are the new unique findings that would have otherwise been posted: #{unique_results}"
return
end
create_inline_comments(unique_results)
rescue StandardError => e
@ -141,6 +147,11 @@ class SemgrepResultProcessor
end
end
def sast_stop_label_present?
labels = ENV['CI_MERGE_REQUEST_LABELS'] || ""
labels.split(',').map(&:strip).include?('appsec-sast::stop')
end
private
def get_existing_comments

View File

@ -467,6 +467,7 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
fill_in 'application_setting_auto_devops_domain', with: 'domain.com'
uncheck 'Keep the latest artifacts for all jobs in the latest successful pipelines'
uncheck 'Enable pipeline suggestion banner'
uncheck 'Show the migrate from Jenkins banner'
fill_in 'application_setting_ci_max_includes', with: 200
fill_in 'application_setting_downstream_pipeline_trigger_limit_per_project_user_sha', with: 500
click_button 'Save changes'
@ -476,6 +477,7 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
expect(current_settings.auto_devops_domain).to eq('domain.com')
expect(current_settings.keep_latest_artifact).to be false
expect(current_settings.suggest_pipeline_enabled).to be false
expect(current_settings.show_migrate_from_jenkins_banner).to be false
expect(current_settings.ci_max_includes).to be 200
expect(current_settings.downstream_pipeline_trigger_limit_per_project_user_sha).to be 500
expect(page).to have_content 'Application settings saved successfully'

View File

@ -1,6 +1,9 @@
import { GlSprintf } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { triggerEvent } from 'helpers/tracking_helper';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { InternalEvents } from '~/tracking';
import migrateJenkinsComponent from '~/vue_merge_request_widget/components/mr_widget_migrate_jenkins.vue';
import { JM_EVENT_NAME, JM_MIGRATION_LINK } from '~/vue_merge_request_widget/constants';
@ -8,20 +11,33 @@ import { JM_EVENT_NAME, JM_MIGRATION_LINK } from '~/vue_merge_request_widget/con
describe('MrWidgetMigrateJenkins', () => {
describe('template', () => {
let wrapper;
let mockAxios;
const props = {
humanAccess: 'maintainer',
path: 'some/path',
featureId: 'some-feature-id',
};
describe('core functionality', () => {
const findCloseButton = () => wrapper.find('[data-testid="close"]');
const migrationPlanLink = () => wrapper.find('[data-testid="migration-plan"]');
beforeEach(() => {
const createComponent = (propsData = { ...props }) => {
wrapper = mount(migrateJenkinsComponent, {
propsData: {
humanAccess: 'maintainer',
},
propsData,
stubs: {
GlSprintf,
},
});
};
beforeEach(() => {
createComponent();
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
mockAxios.restore();
});
it('renders the expected text', () => {
@ -39,6 +55,8 @@ describe('MrWidgetMigrateJenkins', () => {
});
it('emits an event when the close button is clicked', async () => {
mockAxios.onPost(props.path).replyOnce(HTTP_STATUS_OK);
const closeButton = findCloseButton();
await closeButton.trigger('click');
@ -47,6 +65,8 @@ describe('MrWidgetMigrateJenkins', () => {
describe('tracking', () => {
it('sends an event when the close button is clicked', () => {
mockAxios.onPost(props.path).replyOnce(HTTP_STATUS_OK);
jest.spyOn(InternalEvents, 'trackEvent');
const okBtn = findCloseButton();

View File

@ -0,0 +1,233 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::VirtualRegistries::Packages::Maven, :aggregate_failures, feature_category: :virtual_registry do
using RSpec::Parameterized::TableSyntax
include_context 'for maven virtual registry api setup'
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id/cached_responses' do
let(:upstream_id) { upstream.id }
let(:url) do
"/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream_id}/cached_responses"
end
let_it_be(:processing_cached_response) do
create(
:virtual_registries_packages_maven_cached_response,
:processing,
upstream: upstream,
group: upstream.group,
relative_path: cached_response.relative_path
)
end
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to contain_exactly(
cached_response
.as_json
.merge('cached_response_id' => Base64.urlsafe_encode64(cached_response.relative_path))
.except('id', 'object_storage_key', 'file_store', 'status', 'file_final_path')
)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with invalid upstream' do
where(:upstream_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'with a non-member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :forbidden
'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for search param' do
let(:url) { "#{super()}?search=#{search}" }
let(:valid_search) { cached_response.relative_path.slice(0, 5) }
where(:search, :status) do
ref(:valid_search) | :ok
'foo' | :empty
'' | :ok
nil | :ok
end
with_them do
if params[:status] == :ok
it_behaves_like 'successful response'
else
it 'returns an empty array' do
api_request
expect(json_response).to eq([])
end
end
end
end
end
describe 'DELETE /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/' \
':upstream_id/cached_responses/:cached_response_id' do
let(:cached_response_id) { Base64.urlsafe_encode64(cached_response.relative_path) }
let(:url) do
"/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}/" \
"cached_responses/#{cached_response_id}"
end
let_it_be(:processing_cached_response) do
create(
:virtual_registries_packages_maven_cached_response,
:processing,
upstream: upstream,
group: upstream.group,
relative_path: cached_response.relative_path
)
end
subject(:api_request) { delete api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { upstream.cached_responses.count }.by(-1)
expect(response).to have_gitlab_http_status(:no_content)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'for different user roles' do
where(:user_role, :status) do
:owner | :no_content
:maintainer | :no_content
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :no_content
:personal_access_token | :basic_auth | :no_content
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :no_content
:job_token | :basic_auth | :no_content
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'when error occurs' do
before_all do
group.add_maintainer(user)
end
before do
allow_next_found_instance_of(cached_response.class) do |instance|
errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:cached_response, 'error message') }
allow(instance).to receive_messages(save: false, errors: errors)
end
end
it 'returns an error' do
api_request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'message' => { 'cached_response' => ['error message'] } })
end
end
end
end

View File

@ -0,0 +1,385 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::VirtualRegistries::Packages::Maven, :aggregate_failures, feature_category: :virtual_registry do
using RSpec::Parameterized::TableSyntax
include_context 'for maven virtual registry api setup'
describe 'GET /api/v4/virtual_registries/packages/maven/registries' do
let(:group_id) { group.id }
let(:url) { "/virtual_registries/packages/maven/registries?group_id=#{group_id}" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to contain_exactly(registry.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid group_id' do
it_behaves_like 'successful response'
end
context 'with invalid group_id' do
where(:group_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'with missing group_id' do
let(:url) { '/virtual_registries/packages/maven/registries' }
it 'returns a bad request with missing group_id' do
api_request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('group_id is missing, group_id is empty')
end
end
context 'with a non member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :forbidden
'INTERNAL' | :forbidden
'PRIVATE' | :not_found
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'POST /api/v4/virtual_registries/packages/maven/registries' do
let_it_be(:registry_class) { ::VirtualRegistries::Packages::Maven::Registry }
let(:url) { '/virtual_registries/packages/maven/registries' }
subject(:api_request) { post api(url), headers: headers, params: params }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { registry_class.count }.by(1)
expect(registry_class.last.group_id).to eq(params[:group_id])
end
end
context 'with valid params' do
let(:params) { { group_id: group.id } }
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
where(:user_role, :status) do
:owner | :created
:maintainer | :created
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
registry_class.for_group(group).delete_all
group.send(:"add_#{user_role}", user)
end
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
context 'with existing registry' do
before_all do
group.add_maintainer(user)
end
it 'returns a bad request' do
api_request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'message' => { 'group' => ['has already been taken'] } })
end
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
before do
registry_class.for_group(group).delete_all
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :created
:personal_access_token | :basic_auth | :created
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :created
:job_token | :basic_auth | :created
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with invalid params' do
before_all do
group.add_maintainer(user)
end
where(:group_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
let(:params) { { group_id: group_id } }
it_behaves_like 'returning response status', params[:status]
end
end
context 'with subgroup' do
let(:subgroup) { create(:group, parent: group, visibility_level: group.visibility_level) }
let(:params) { { group_id: subgroup.id } }
before_all do
group.add_maintainer(user)
end
it 'returns a bad request beacuse it is not a top level group' do
api_request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'message' => { 'group' => ['must be a top level Group'] } })
end
end
end
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to eq(registry.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid registry_id' do
it_behaves_like 'successful response'
end
context 'with invalid registry_id' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'with a non member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :forbidden
'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'DELETE /api/v4/virtual_registries/packages/maven/registries/:id' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}" }
subject(:api_request) { delete api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Registry.count }.by(-1)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid registry_id' do
where(:user_role, :status) do
:owner | :no_content
:maintainer | :no_content
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with invalid registry_id' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :not_found
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :no_content
:personal_access_token | :basic_auth | :no_content
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :no_content
:job_token | :basic_auth | :no_content
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,447 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::VirtualRegistries::Packages::Maven, :aggregate_failures, feature_category: :virtual_registry do
using RSpec::Parameterized::TableSyntax
include_context 'for maven virtual registry api setup'
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to contain_exactly(registry.upstream.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid registry' do
it_behaves_like 'successful response'
end
context 'with invalid registry' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'with a non member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :forbidden
'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'POST /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" }
let(:params) { { url: 'http://example.com' } }
subject(:api_request) { post api(url), headers: headers, params: params }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Upstream.count }.by(1)
.and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(1)
expect(::VirtualRegistries::Packages::Maven::Upstream.last.cache_validity_hours).to eq(
params[:cache_validity_hours] || ::VirtualRegistries::Packages::Maven::Upstream.new.cache_validity_hours
)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid params' do
where(:user_role, :status) do
:owner | :created
:maintainer | :created
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
registry.upstream&.destroy!
group.send(:"add_#{user_role}", user)
end
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with invalid registry' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :not_found
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'for params' do
where(:params, :status) do
{ url: 'http://example.com', username: 'test', password: 'test', cache_validity_hours: 3 } | :created
{ url: 'http://example.com', username: 'test', password: 'test' } | :created
{ url: '', username: 'test', password: 'test' } | :bad_request
{ url: 'http://example.com', username: 'test' } | :bad_request
{} | :bad_request
end
before do
registry.upstream&.destroy!
end
before_all do
group.add_maintainer(user)
end
with_them do
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with existing upstream' do
before_all do
group.add_maintainer(user)
create(:virtual_registries_packages_maven_upstream, registry: registry)
end
it_behaves_like 'returning response status', :conflict
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
before do
registry.upstream&.destroy!
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :created
:personal_access_token | :basic_auth | :created
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :created
:job_token | :basic_auth | :created
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
end
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to eq(registry.upstream.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'with valid params' do
it_behaves_like 'successful response'
end
context 'with a non member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :forbidden
'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
it_behaves_like 'returning response status', params[:status]
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'PATCH /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { patch api(url), params: params, headers: headers }
context 'with valid params' do
let(:params) { { url: 'http://example.com', username: 'test', password: 'test' } }
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
where(:user_role, :status) do
:owner | :ok
:maintainer | :ok
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
it_behaves_like 'returning response status', params[:status]
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for params' do
before_all do
group.add_maintainer(user)
end
let(:params) do
{ url: param_url, username: username, password: password, cache_validity_hours: cache_validity_hours }.compact
end
where(:param_url, :username, :password, :cache_validity_hours, :status) do
nil | 'test' | 'test' | 3 | :ok
'http://example.com' | nil | 'test' | 3 | :ok
'http://example.com' | 'test' | nil | 3 | :ok
'http://example.com' | 'test' | 'test' | nil | :ok
nil | nil | nil | 3 | :ok
'http://example.com' | 'test' | 'test' | 3 | :ok
'' | 'test' | 'test' | 3 | :bad_request
'http://example.com' | '' | 'test' | 3 | :bad_request
'http://example.com' | 'test' | '' | 3 | :bad_request
'http://example.com' | 'test' | 'test' | -1 | :bad_request
nil | nil | nil | nil | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'DELETE /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { delete api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Upstream.count }.by(-1)
.and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(-1)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled virtual_registry_maven feature flag'
it_behaves_like 'maven virtual registry disabled dependency proxy'
it_behaves_like 'maven virtual registry not authenticated user'
context 'for different user roles' do
where(:user_role, :status) do
:owner | :no_content
:maintainer | :no_content
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :no_content
:personal_access_token | :basic_auth | :no_content
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :no_content
:job_token | :basic_auth | :no_content
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
end
end

View File

@ -53,6 +53,50 @@ RSpec.describe SemgrepResultProcessor, feature_category: :tooling do
expect { processor.execute }.to raise_error(SystemExit)
end
context 'when CI_MERGE_REQUEST_LABELS includes appsec-sast::stop' do
it "prints the 'not adding comments' message" do
stub_env('CI_MERGE_REQUEST_LABELS', 'appsec-sast::stop')
expect(processor).to receive(:perform_allowlist_check)
expect(processor).to receive(:get_sast_results)
expect(processor).to receive(:filter_duplicate_findings).with(sample_results)
expect do
processor.execute
end.to output(/Not adding comments for this MR as it has the appsec-sast::stop label/).to_stdout
end
end
end
describe '#sast_stop_label_present?' do
context 'when CI_MERGE_REQUEST_LABELS includes appsec-sast::stop' do
it 'returns true' do
stub_env('CI_MERGE_REQUEST_LABELS', 'appsec-sast::stop, other-label')
expect(processor.sast_stop_label_present?).to be true
end
end
context 'when CI_MERGE_REQUEST_LABELS does not include appsec-sast::stop' do
it 'returns false' do
stub_env('CI_MERGE_REQUEST_LABELS', 'another-label, different-label')
expect(processor.sast_stop_label_present?).to be false
end
end
context 'when CI_MERGE_REQUEST_LABELS is empty' do
it 'returns false' do
stub_env('CI_MERGE_REQUEST_LABELS', '')
expect(processor.sast_stop_label_present?).to be false
end
end
context 'when CI_MERGE_REQUEST_LABELS is nil' do
it 'returns false' do
stub_env('CI_MERGE_REQUEST_LABELS', nil)
expect(processor.sast_stop_label_present?).to be false
end
end
end
describe '#perform_allowlist_check' do

View File

@ -48,6 +48,18 @@ RSpec.describe Packages::TerraformModule::Metadata::ExtractFilesService, feature
it { expect(subject.payload).to eq(metadata) }
end
shared_examples 'extracting metadata from README files only' do
let(:metadata) { super().tap { |metadata| metadata[:root].slice!(:readme) } }
before do
allow_next_instance_of(::Packages::TerraformModule::Metadata::ParseHclFileService) do |parser|
allow(parser).to receive(:execute).and_raise(StandardError)
end
end
it_behaves_like 'extracting metadata'
end
shared_examples 'raising too many files error' do
context 'with too many files' do
before do
@ -159,6 +171,10 @@ RSpec.describe Packages::TerraformModule::Metadata::ExtractFilesService, feature
it_behaves_like 'extracting metadata'
end
context 'when a processing error occurs druing HCL file parsing' do
it_behaves_like 'extracting metadata from README files only'
end
end
context 'when processing a zip archive' do
@ -179,6 +195,10 @@ RSpec.describe Packages::TerraformModule::Metadata::ExtractFilesService, feature
it_behaves_like 'raising too many files error'
it_behaves_like 'aggregating metadata'
context 'when a processing error occurs druing HCL file parsing' do
it_behaves_like 'extracting metadata from README files only'
end
end
context 'for getting module_type from path' do

View File

@ -240,6 +240,8 @@ RSpec.describe Packages::TerraformModule::Metadata::ProcessFileService, feature_
execute
end
it_behaves_like 'returning an error service response', message: 'Error processing path'
end
end
end

View File

@ -10,7 +10,8 @@ RSpec.describe WorkItems::DataSync::CloneService, feature_category: :team_planni
let_it_be(:source_project_member) { create(:user, reporter_of: project) }
let_it_be(:target_project_member) { create(:user, reporter_of: target_project) }
let_it_be(:projects_member) { create(:user, reporter_of: [project, target_project]) }
let_it_be_with_reload(:target_namespace) { target_project.project_namespace }
let_it_be_with_refind(:target_namespace) { target_project.project_namespace }
let(:service) do
described_class.new(

View File

@ -4,13 +4,14 @@ require 'spec_helper'
RSpec.describe WorkItems::DataSync::MoveService, feature_category: :team_planning do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:target_project) { create(:project, group: group) }
let_it_be_with_reload(:original_work_item) { create(:work_item, :opened, project: project) }
let_it_be(:source_project_member) { create(:user, reporter_of: project) }
let_it_be(:target_project_member) { create(:user, reporter_of: target_project) }
let_it_be(:projects_member) { create(:user, reporter_of: [project, target_project]) }
let_it_be_with_reload(:target_namespace) { target_project.project_namespace }
let_it_be_with_refind(:target_namespace) { target_project.project_namespace }
let(:service) do
described_class.new(

View File

@ -0,0 +1,112 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WorkItems::DataSync::Widgets::CrmContacts, feature_category: :team_planning do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project, group: create(:group)) }
let_it_be(:work_item) { create(:work_item, project: project) }
let_it_be(:target_work_item) { create(:work_item, project: project) }
let_it_be(:issue_contact1) { create(:issue_customer_relations_contact, :for_issue, issue: work_item) }
let_it_be(:issue_contact2) { create(:issue_customer_relations_contact, :for_issue, issue: work_item) }
let(:params) { {} }
subject(:callback) do
described_class.new(
work_item: work_item, target_work_item: target_work_item, current_user: current_user, params: params
)
end
describe '#after_save_commit' do
context 'when target work item has crm_contacts widget' do
before do
allow(target_work_item).to receive(:get_widget).with(:crm_contacts).and_return(true)
end
context "when target work item namespace have same crm_group" do
it 'copies the contacts from work_item to target_work_item' do
crm_contacts = work_item.customer_relations_contacts
callback.after_save_commit
expect(target_work_item.reload.customer_relations_contacts).to match_array(crm_contacts)
end
context "when the operation is move" do
let(:params) { { operation: :move } }
it "keeps the created_at and updated_at values of the issue_customer_relations_contacts" do
callback.after_save_commit
target_work_item.issue_customer_relations_contacts.each do |target_issue_contact|
word_item_contact = work_item.issue_customer_relations_contacts
.find_by(contact_id: target_issue_contact.contact_id)
expect(target_issue_contact.created_at).to eq(word_item_contact.created_at)
expect(target_issue_contact.updated_at).to eq(word_item_contact.updated_at)
end
end
end
context "when the operation is clone" do
let(:params) { { operation: :clone } }
it "clones the crm contacts" do
crm_contacts = work_item.customer_relations_contacts
callback.after_save_commit
expect(target_work_item.reload.customer_relations_contacts).to match_array(crm_contacts)
end
end
end
context "when target work item namespace does not have same crm_group" do
let_it_be(:target_work_item) { create(:work_item, project: create(:project)) }
it 'does not copy crm_contacts' do
expect(work_item.customer_relations_contacts).not_to be_empty
callback.after_save_commit
expect(target_work_item.reload.customer_relations_contacts).to be_empty
end
it "creates a note with removed contacts quote", :aggregate_failures do
expect(target_work_item.reload.notes).to be_empty
callback.after_save_commit
note = target_work_item.notes.first.note
expect(target_work_item.reload.notes).not_to be_empty
expect(note).to include("removed 2 contacts")
end
end
end
context 'when target work item does not have crm_contacts widget' do
before do
allow(target_work_item).to receive(:get_widget).with(:crm_contacts).and_return(false)
end
it 'does not copy crm_contacts' do
expect(work_item.customer_relations_contacts).not_to be_empty
callback.after_save_commit
expect(target_work_item.reload.customer_relations_contacts).to be_empty
end
end
end
describe '#post_move_cleanup' do
it 'clears the crm_contacts from the original work item' do
expect(work_item.customer_relations_contacts).not_to be_empty
callback.post_move_cleanup
expect(work_item.reload.customer_relations_contacts).to be_empty
end
end
end

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
RSpec.shared_context 'for maven virtual registry api setup' do
include WorkhorseHelpers
include HttpBasicAuthHelpers
let_it_be(:group) { create(:group) }
let_it_be_with_reload(:registry) { create(:virtual_registries_packages_maven_registry, group: group) }
let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream, registry: registry) }
let_it_be_with_reload(:cached_response) do
create(:virtual_registries_packages_maven_cached_response, upstream: upstream)
end
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:user) { create(:user, owner_of: project) }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
let_it_be(:deploy_token) do
create(:deploy_token, :group, groups: [group], read_virtual_registry: true)
end
let(:personal_access_token) { create(:personal_access_token, user: user) }
let(:headers) { user_basic_auth_header(user, personal_access_token) }
before do
stub_config(dependency_proxy: { enabled: true }) # not enabled by default
end
def token_header(token)
case token
when :personal_access_token
{ 'PRIVATE-TOKEN' => personal_access_token.token }
when :deploy_token
{ 'Deploy-Token' => deploy_token.token }
when :job_token
{ 'Job-Token' => job.token }
end
end
def token_basic_auth(token)
case token
when :personal_access_token
user_basic_auth_header(user, personal_access_token)
when :deploy_token
deploy_token_basic_auth_header(deploy_token)
when :job_token
job_basic_auth_header(job)
end
end
end

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
RSpec.shared_examples 'disabled virtual_registry_maven feature flag' do
before do
stub_feature_flags(virtual_registry_maven: false)
end
it_behaves_like 'returning response status', :not_found
end
RSpec.shared_examples 'maven virtual registry disabled dependency proxy' do
before do
stub_config(dependency_proxy: { enabled: false })
end
it_behaves_like 'returning response status', :not_found
end
RSpec.shared_examples 'maven virtual registry not authenticated user' do
let(:headers) { {} }
it_behaves_like 'returning response status', :unauthorized
end
RSpec.shared_examples 'maven virtual registry authenticated endpoint' do |success_shared_example_name:|
%i[personal_access_token deploy_token job_token].each do |token_type|
context "with a #{token_type}" do
let_it_be(:user) { deploy_token } if token_type == :deploy_token
context 'when sent by headers' do
let(:headers) { super().merge(token_header(token_type)) }
it_behaves_like success_shared_example_name
end
context 'when sent by basic auth' do
let(:headers) { super().merge(token_basic_auth(token_type)) }
it_behaves_like success_shared_example_name
end
end
end
end

View File

@ -88,6 +88,10 @@ RSpec.shared_examples 'cloneable and moveable widget data' do
work_item.reload.sent_notifications.pluck(:recipient_id)
end
def work_item_crm_contacts(work_item)
work_item.reload.customer_relations_contacts
end
let(:move) { WorkItems::DataSync::MoveService }
let(:clone) { WorkItems::DataSync::CloneService }
@ -110,6 +114,28 @@ RSpec.shared_examples 'cloneable and moveable widget data' do
original_work_item.reload.sent_notifications.pluck(:recipient_id)
end
let_it_be(:crm_contacts) do
# create a crm group and assign it to both original and new work item namespaces in order to be able to move the
# crm contacts from the original one to the new one
crm_group = create(:group)
create(:crm_settings, group: group, source_group: crm_group)
# The target_namespace can be a `Group`, `Namespaces::ProjectNamespace` or `Project`, we fetch the group, based
# on the namespace type. Also we check that the `target group`` is different that the group, as there will
# be validation errors when we create the crm_settings
if target_namespace.is_a?(Group)
create(:crm_settings, group: target_namespace, source_group: crm_group) if target_namespace != group
elsif target_namespace.is_a?(Namespaces::ProjectNamespace) || target_namespace.is_a?(Project)
create(:crm_settings, group: target_namespace.group, source_group: crm_group) if target_namespace.group != group
end
contacts = create_list(:contact, 2, group: crm_group)
original_work_item.customer_relations_contacts << contacts
# set the crm_contacts on the before_all call and return the contacts as `expected_data` for later comparison as the
# cleanup callback will delete the association
contacts
end
let_it_be(:emails) do
create_list(:issue_email_participant, 2, issue: original_work_item)
# create email participants on original work item and return emails as `expected_data` for later comparison.
@ -150,6 +176,7 @@ RSpec.shared_examples 'cloneable and moveable widget data' do
:milestone | :work_item_milestone | ref(:milestone) | [ref(:move), ref(:clone)]
:subscriptions | :work_item_subscriptions | ref(:subscriptions) | [ref(:move)]
:sent_notifications | :work_item_sent_notifications | ref(:notifications) | [ref(:move)]
:customer_relations_contacts | :work_item_crm_contacts | ref(:crm_contacts) | [ref(:move), ref(:clone)]
end
with_them do
@ -158,7 +185,7 @@ RSpec.shared_examples 'cloneable and moveable widget data' do
allow(original_work_item).to receive(:from_service_desk?).and_return(true)
end
it 'clones and moves widget data' do
it 'clones and moves widget data', :aggregate_failures do
new_work_item = service.execute[:work_item]
widget_value = send(eval_value, new_work_item)