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/settings_spec.rb'
- 'spec/requests/api/users_preferences_spec.rb' - 'spec/requests/api/users_preferences_spec.rb'
- 'spec/requests/api/users_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/groups/deploy_tokens_controller_spec.rb'
- 'spec/requests/openid_connect_spec.rb' - 'spec/requests/openid_connect_spec.rb'
- 'spec/requests/organizations/organizations_controller_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/helpers_spec.rb'
- 'spec/requests/api/maven_packages_spec.rb' - 'spec/requests/api/maven_packages_spec.rb'
- 'spec/requests/api/search_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/databases_controller_spec.rb'
- 'spec/requests/projects/google_cloud/service_accounts_controller_spec.rb' - 'spec/requests/projects/google_cloud/service_accounts_controller_spec.rb'
- 'spec/requests/projects/redirect_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-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-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_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-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_job","version":"0.7.8","platform":"ruby","checksum":"281b3d9bace7aac9f1e121b75f294e7ee8beaedd1f20405539c54df2e6763942"},
{"name":"opentelemetry-instrumentation-active_record","version":"0.8.0","platform":"ruby","checksum":"199d83102e81f5d6236025f4e6507513ffd2e6a2231e9951d0723708cc96c1c8"}, {"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-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-pg","version":"0.29.0","platform":"ruby","checksum":"6fcbdddfb757fed97b3bce0fb9f5f206d4cdb5c24e3da78c0dc54100c195f3d1"},
{"name":"opentelemetry-instrumentation-rack","version":"0.25.0","platform":"ruby","checksum":"539c8b4f6b818e16495e9016126537b4d1cc53292219f1acc35006fe30ce243d"}, {"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-rake","version":"0.2.2","platform":"ruby","checksum":"fbde8a6aab77c09bf0f94d914dd26dcf2e23ec67e2300f06a1cb8294a97d8020"},
{"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"}, {"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"},
{"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"}, {"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"},

View File

@ -1291,7 +1291,7 @@ GEM
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.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-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21) opentelemetry-instrumentation-rack (~> 0.21)
@ -1349,10 +1349,10 @@ GEM
opentelemetry-instrumentation-rack (0.25.0) opentelemetry-instrumentation-rack (0.25.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.32.0) opentelemetry-instrumentation-rails (0.33.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.2.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-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0) opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.8.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-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-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_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-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_job","version":"0.7.8","platform":"ruby","checksum":"281b3d9bace7aac9f1e121b75f294e7ee8beaedd1f20405539c54df2e6763942"},
{"name":"opentelemetry-instrumentation-active_record","version":"0.8.0","platform":"ruby","checksum":"199d83102e81f5d6236025f4e6507513ffd2e6a2231e9951d0723708cc96c1c8"}, {"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-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-pg","version":"0.29.0","platform":"ruby","checksum":"6fcbdddfb757fed97b3bce0fb9f5f206d4cdb5c24e3da78c0dc54100c195f3d1"},
{"name":"opentelemetry-instrumentation-rack","version":"0.25.0","platform":"ruby","checksum":"539c8b4f6b818e16495e9016126537b4d1cc53292219f1acc35006fe30ce243d"}, {"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-rake","version":"0.2.2","platform":"ruby","checksum":"fbde8a6aab77c09bf0f94d914dd26dcf2e23ec67e2300f06a1cb8294a97d8020"},
{"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"}, {"name":"opentelemetry-instrumentation-redis","version":"0.25.7","platform":"ruby","checksum":"2ea0f2d45fe1af0689aeadc08f5b335a2b6d9463de9d855fd25313d3c5b42fe3"},
{"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"}, {"name":"opentelemetry-instrumentation-sidekiq","version":"0.25.7","platform":"ruby","checksum":"d6a6e2cadddfda0a0b641f9dc918e35a77bfc62bc90b80776f5194bd55e0df31"},

View File

@ -1306,7 +1306,7 @@ GEM
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.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-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21) opentelemetry-instrumentation-rack (~> 0.21)
@ -1364,10 +1364,10 @@ GEM
opentelemetry-instrumentation-rack (0.25.0) opentelemetry-instrumentation-rack (0.25.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1) opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.32.0) opentelemetry-instrumentation-rails (0.33.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.2.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-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0) opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.8.0) opentelemetry-instrumentation-active_record (~> 0.8.0)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class CustomerRelations::IssueContact < ApplicationRecord class CustomerRelations::IssueContact < ApplicationRecord
include EachBatch
self.table_name = "issue_customer_relations_contacts" self.table_name = "issue_customer_relations_contacts"
belongs_to :issue, optional: false, inverse_of: :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 :execute_hooks, :execute_integrations, :group, to: :project, allow_nil: true
delegate :external_references_supported?, :default_issues_tracker?, :pending_delete?, to: :project delegate :external_references_supported?, :default_issues_tracker?, :pending_delete?, to: :project
delegate :service_desk_alias_address, to: :project delegate :service_desk_alias_address, to: :project
delegate :crm_group, to: :project
def self.sti_name def self.sti_name
'Project' 'Project'

View File

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

View File

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

View File

@ -5,14 +5,41 @@ module WorkItems
module Widgets module Widgets
class CrmContacts < Base class CrmContacts < Base
def after_save_commit def after_save_commit
# copy contacts, e.g. return unless target_work_item.get_widget(:crm_contacts)
# return unless work_item.namespace.root_ancestor == target_work_item.namespace.root_ancestor
# if work_item.namespace.crm_group == target_work_item.namespace.crm_group
# target_work_item.customer_relations_contacts = work_item.customer_relations_contacts 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 end
def post_move_cleanup 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 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' = 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 .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.') = 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 } #js-runner-token-expiration-intervals{ data: runner_token_expiration_interval_attributes }
.form-group .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.') = 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: identifiers:
- project - project
- user - user
product_group: threat_insights product_group: security_insights
milestone: '14.1' milestone: '14.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166618 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166618
distributions: distributions:

View File

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

View File

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

View File

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

View File

@ -59,6 +59,7 @@
"runner", "runner",
"scalability", "scalability",
"secret_detection", "secret_detection",
"security_insights",
"security_policies", "security_policies",
"source_code", "source_code",
"static_analysis", "static_analysis",
@ -66,7 +67,6 @@
"switchboard", "switchboard",
"technical_writing", "technical_writing",
"tenant_scale", "tenant_scale",
"threat_insights",
"utilization", "utilization",
"ux_paper_cuts", "ux_paper_cuts",
"vulnerability_research" "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, integrations jsonb DEFAULT '{}'::jsonb NOT NULL,
user_seat_management jsonb DEFAULT '{}'::jsonb NOT NULL, user_seat_management jsonb DEFAULT '{}'::jsonb NOT NULL,
resource_usage_limits 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_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_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)), 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. Clear the **Enable pipeline suggestion banner** checkbox.
1. Select **Save changes**. 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 ## Required pipeline configuration
DETAILS: 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 :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 :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 :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 :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 :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' 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." msgid "AdminSettings|Setting must be greater than 0."
msgstr "" 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." msgid "AdminSettings|Show a redirect page that warns you about user-generated content in GitLab Pages."
msgstr "" 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}" msgid "AdminSettings|Silent exports by admins %{silent_admin_exports_link_start}%{icon}%{silent_admin_exports_link_end}"
msgstr "" msgstr ""

View File

@ -17,6 +17,7 @@ class SemgrepResultProcessor
<small> <small>
This AppSec automation is currently under testing. This AppSec automation is currently under testing.
Use ~"appsec-sast::helpful" or ~"appsec-sast::unhelpful" for quick feedback. 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). For any detailed feedback, [add a comment here](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/issues/38).
</small> </small>
@ -30,6 +31,11 @@ class SemgrepResultProcessor
perform_allowlist_check perform_allowlist_check
semgrep_results = get_sast_results semgrep_results = get_sast_results
unique_results = filter_duplicate_findings(semgrep_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) create_inline_comments(unique_results)
rescue StandardError => e rescue StandardError => e
@ -141,6 +147,11 @@ class SemgrepResultProcessor
end end
end end
def sast_stop_label_present?
labels = ENV['CI_MERGE_REQUEST_LABELS'] || ""
labels.split(',').map(&:strip).include?('appsec-sast::stop')
end
private private
def get_existing_comments 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' fill_in 'application_setting_auto_devops_domain', with: 'domain.com'
uncheck 'Keep the latest artifacts for all jobs in the latest successful pipelines' uncheck 'Keep the latest artifacts for all jobs in the latest successful pipelines'
uncheck 'Enable pipeline suggestion banner' 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_ci_max_includes', with: 200
fill_in 'application_setting_downstream_pipeline_trigger_limit_per_project_user_sha', with: 500 fill_in 'application_setting_downstream_pipeline_trigger_limit_per_project_user_sha', with: 500
click_button 'Save changes' 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.auto_devops_domain).to eq('domain.com')
expect(current_settings.keep_latest_artifact).to be false expect(current_settings.keep_latest_artifact).to be false
expect(current_settings.suggest_pipeline_enabled).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.ci_max_includes).to be 200
expect(current_settings.downstream_pipeline_trigger_limit_per_project_user_sha).to be 500 expect(current_settings.downstream_pipeline_trigger_limit_per_project_user_sha).to be 500
expect(page).to have_content 'Application settings saved successfully' expect(page).to have_content 'Application settings saved successfully'

View File

@ -1,6 +1,9 @@
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { triggerEvent } from 'helpers/tracking_helper'; 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 { InternalEvents } from '~/tracking';
import migrateJenkinsComponent from '~/vue_merge_request_widget/components/mr_widget_migrate_jenkins.vue'; 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'; 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('MrWidgetMigrateJenkins', () => {
describe('template', () => { describe('template', () => {
let wrapper; let wrapper;
let mockAxios;
const props = {
humanAccess: 'maintainer',
path: 'some/path',
featureId: 'some-feature-id',
};
describe('core functionality', () => { describe('core functionality', () => {
const findCloseButton = () => wrapper.find('[data-testid="close"]'); const findCloseButton = () => wrapper.find('[data-testid="close"]');
const migrationPlanLink = () => wrapper.find('[data-testid="migration-plan"]'); const migrationPlanLink = () => wrapper.find('[data-testid="migration-plan"]');
beforeEach(() => { const createComponent = (propsData = { ...props }) => {
wrapper = mount(migrateJenkinsComponent, { wrapper = mount(migrateJenkinsComponent, {
propsData: { propsData,
humanAccess: 'maintainer',
},
stubs: { stubs: {
GlSprintf, GlSprintf,
}, },
}); });
};
beforeEach(() => {
createComponent();
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
mockAxios.restore();
}); });
it('renders the expected text', () => { it('renders the expected text', () => {
@ -39,6 +55,8 @@ describe('MrWidgetMigrateJenkins', () => {
}); });
it('emits an event when the close button is clicked', async () => { it('emits an event when the close button is clicked', async () => {
mockAxios.onPost(props.path).replyOnce(HTTP_STATUS_OK);
const closeButton = findCloseButton(); const closeButton = findCloseButton();
await closeButton.trigger('click'); await closeButton.trigger('click');
@ -47,6 +65,8 @@ describe('MrWidgetMigrateJenkins', () => {
describe('tracking', () => { describe('tracking', () => {
it('sends an event when the close button is clicked', () => { it('sends an event when the close button is clicked', () => {
mockAxios.onPost(props.path).replyOnce(HTTP_STATUS_OK);
jest.spyOn(InternalEvents, 'trackEvent'); jest.spyOn(InternalEvents, 'trackEvent');
const okBtn = findCloseButton(); 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) expect { processor.execute }.to raise_error(SystemExit)
end 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 end
describe '#perform_allowlist_check' do 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) } it { expect(subject.payload).to eq(metadata) }
end 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 shared_examples 'raising too many files error' do
context 'with too many files' do context 'with too many files' do
before do before do
@ -159,6 +171,10 @@ RSpec.describe Packages::TerraformModule::Metadata::ExtractFilesService, feature
it_behaves_like 'extracting metadata' it_behaves_like 'extracting metadata'
end end
context 'when a processing error occurs druing HCL file parsing' do
it_behaves_like 'extracting metadata from README files only'
end
end end
context 'when processing a zip archive' do 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 'raising too many files error'
it_behaves_like 'aggregating metadata' 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 end
context 'for getting module_type from path' do context 'for getting module_type from path' do

View File

@ -240,6 +240,8 @@ RSpec.describe Packages::TerraformModule::Metadata::ProcessFileService, feature_
execute execute
end end
it_behaves_like 'returning an error service response', message: 'Error processing path'
end end
end 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(:source_project_member) { create(:user, reporter_of: project) }
let_it_be(:target_project_member) { create(:user, reporter_of: target_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(: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 let(:service) do
described_class.new( described_class.new(

View File

@ -4,13 +4,14 @@ require 'spec_helper'
RSpec.describe WorkItems::DataSync::MoveService, feature_category: :team_planning do RSpec.describe WorkItems::DataSync::MoveService, feature_category: :team_planning do
let_it_be(:group) { create(:group) } 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(:target_project) { create(:project, group: group) }
let_it_be_with_reload(:original_work_item) { create(:work_item, :opened, project: project) } 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(:source_project_member) { create(:user, reporter_of: project) }
let_it_be(:target_project_member) { create(:user, reporter_of: target_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(: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 let(:service) do
described_class.new( 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) work_item.reload.sent_notifications.pluck(:recipient_id)
end end
def work_item_crm_contacts(work_item)
work_item.reload.customer_relations_contacts
end
let(:move) { WorkItems::DataSync::MoveService } let(:move) { WorkItems::DataSync::MoveService }
let(:clone) { WorkItems::DataSync::CloneService } 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) original_work_item.reload.sent_notifications.pluck(:recipient_id)
end 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 let_it_be(:emails) do
create_list(:issue_email_participant, 2, issue: original_work_item) 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. # 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)] :milestone | :work_item_milestone | ref(:milestone) | [ref(:move), ref(:clone)]
:subscriptions | :work_item_subscriptions | ref(:subscriptions) | [ref(:move)] :subscriptions | :work_item_subscriptions | ref(:subscriptions) | [ref(:move)]
:sent_notifications | :work_item_sent_notifications | ref(:notifications) | [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 end
with_them do 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) allow(original_work_item).to receive(:from_service_desk?).and_return(true)
end end
it 'clones and moves widget data' do it 'clones and moves widget data', :aggregate_failures do
new_work_item = service.execute[:work_item] new_work_item = service.execute[:work_item]
widget_value = send(eval_value, new_work_item) widget_value = send(eval_value, new_work_item)