Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ef0366928
commit
8c438dd7a6
|
@ -144,18 +144,20 @@ review-stop:
|
|||
|
||||
.allure-report-base:
|
||||
image:
|
||||
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.0.6
|
||||
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.3.1
|
||||
entrypoint: [""]
|
||||
stage: post-qa
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
|
||||
GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
|
||||
script:
|
||||
- |
|
||||
allure-report-publisher upload gcs \
|
||||
--results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \
|
||||
--bucket="gitlab-qa-allure-reports" \
|
||||
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
|
||||
--update-pr="comment" \
|
||||
--copy-latest \
|
||||
--color
|
||||
|
||||
|
@ -231,18 +233,20 @@ parallel-spec-reports:
|
|||
allure-report-qa-smoke:
|
||||
extends:
|
||||
- .allure-report-base
|
||||
- .review:rules:review-qa-smoke
|
||||
- .review:rules:review-qa-smoke-report
|
||||
needs: ["review-qa-smoke"]
|
||||
variables:
|
||||
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
|
||||
ALLURE_JOB_NAME: review-qa-smoke
|
||||
|
||||
allure-report-qa-all:
|
||||
extends:
|
||||
- .allure-report-base
|
||||
- .review:rules:review-qa-all
|
||||
- .review:rules:review-qa-all-report
|
||||
needs: ["review-qa-all"]
|
||||
variables:
|
||||
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
|
||||
ALLURE_JOB_NAME: review-qa-all
|
||||
|
||||
danger-review:
|
||||
extends:
|
||||
|
|
|
@ -1130,6 +1130,22 @@
|
|||
changes: *code-qa-patterns
|
||||
allow_failure: true
|
||||
|
||||
.review:rules:review-qa-smoke-report:
|
||||
rules:
|
||||
- <<: *if-not-ee
|
||||
when: never
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *ci-review-patterns
|
||||
when: always
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *frontend-patterns
|
||||
allow_failure: true
|
||||
when: always
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *code-qa-patterns
|
||||
allow_failure: true
|
||||
when: always
|
||||
|
||||
.review:rules:review-qa-all:
|
||||
rules:
|
||||
- <<: *if-not-ee
|
||||
|
@ -1142,6 +1158,19 @@
|
|||
changes: *qa-patterns
|
||||
allow_failure: true
|
||||
|
||||
.review:rules:review-qa-all-report:
|
||||
rules:
|
||||
- <<: *if-not-ee
|
||||
when: never
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *code-patterns
|
||||
when: manual
|
||||
allow_failure: true
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *qa-patterns
|
||||
allow_failure: true
|
||||
when: always
|
||||
|
||||
.review:rules:review-cleanup:
|
||||
rules:
|
||||
- <<: *if-not-ee
|
||||
|
|
|
@ -18,18 +18,27 @@ import {
|
|||
* Translations & helpPagePaths for Static Security Configuration Page
|
||||
*/
|
||||
export const SAST_NAME = __('Static Application Security Testing (SAST)');
|
||||
export const SAST_SHORT_NAME = s__('ciReport|SAST');
|
||||
export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.');
|
||||
export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index');
|
||||
export const SAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/sast/index', {
|
||||
anchor: 'configuration',
|
||||
});
|
||||
|
||||
export const DAST_NAME = __('Dynamic Application Security Testing (DAST)');
|
||||
export const DAST_SHORT_NAME = s__('ciReport|DAST');
|
||||
export const DAST_DESCRIPTION = __('Analyze a review version of your web application.');
|
||||
export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index');
|
||||
export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/dast/index', {
|
||||
anchor: 'enable-dast',
|
||||
});
|
||||
|
||||
export const DAST_PROFILES_NAME = __('DAST Scans');
|
||||
export const DAST_PROFILES_DESCRIPTION = __(
|
||||
'Saved scan settings and target site settings which are reusable.',
|
||||
);
|
||||
export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index');
|
||||
export const DAST_PROFILES_CONFIG_TEXT = s__('SecurityConfiguration|Manage scans');
|
||||
|
||||
export const SECRET_DETECTION_NAME = __('Secret Detection');
|
||||
export const SECRET_DETECTION_DESCRIPTION = __(
|
||||
|
@ -38,6 +47,10 @@ export const SECRET_DETECTION_DESCRIPTION = __(
|
|||
export const SECRET_DETECTION_HELP_PATH = helpPagePath(
|
||||
'user/application_security/secret_detection/index',
|
||||
);
|
||||
export const SECRET_DETECTION_CONFIG_HELP_PATH = helpPagePath(
|
||||
'user/application_security/secret_detection/index',
|
||||
{ anchor: 'configuration' },
|
||||
);
|
||||
|
||||
export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning');
|
||||
export const DEPENDENCY_SCANNING_DESCRIPTION = __(
|
||||
|
@ -46,6 +59,10 @@ export const DEPENDENCY_SCANNING_DESCRIPTION = __(
|
|||
export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
|
||||
'user/application_security/dependency_scanning/index',
|
||||
);
|
||||
export const DEPENDENCY_SCANNING_CONFIG_HELP_PATH = helpPagePath(
|
||||
'user/application_security/dependency_scanning/index',
|
||||
{ anchor: 'configuration' },
|
||||
);
|
||||
|
||||
export const CONTAINER_SCANNING_NAME = __('Container Scanning');
|
||||
export const CONTAINER_SCANNING_DESCRIPTION = __(
|
||||
|
@ -54,6 +71,10 @@ export const CONTAINER_SCANNING_DESCRIPTION = __(
|
|||
export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
|
||||
'user/application_security/container_scanning/index',
|
||||
);
|
||||
export const CONTAINER_SCANNING_CONFIG_HELP_PATH = helpPagePath(
|
||||
'user/application_security/container_scanning/index',
|
||||
{ anchor: 'configuration' },
|
||||
);
|
||||
|
||||
export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing');
|
||||
export const COVERAGE_FUZZING_DESCRIPTION = __(
|
||||
|
@ -136,6 +157,83 @@ export const scanners = [
|
|||
},
|
||||
];
|
||||
|
||||
export const securityFeatures = [
|
||||
{
|
||||
name: SAST_NAME,
|
||||
shortName: SAST_SHORT_NAME,
|
||||
description: SAST_DESCRIPTION,
|
||||
helpPath: SAST_HELP_PATH,
|
||||
configurationHelpPath: SAST_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_SAST,
|
||||
// This field is currently hardcoded because SAST is always available.
|
||||
// It will eventually come from the Backend, the progress is tracked in
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/331622
|
||||
available: true,
|
||||
|
||||
// This field is currently hardcoded because SAST can always be enabled via MR
|
||||
// It will eventually come from the Backend, the progress is tracked in
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/331621
|
||||
canEnableByMergeRequest: true,
|
||||
},
|
||||
{
|
||||
name: DAST_NAME,
|
||||
shortName: DAST_SHORT_NAME,
|
||||
description: DAST_DESCRIPTION,
|
||||
helpPath: DAST_HELP_PATH,
|
||||
configurationHelpPath: DAST_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_DAST,
|
||||
secondary: {
|
||||
type: REPORT_TYPE_DAST_PROFILES,
|
||||
name: DAST_PROFILES_NAME,
|
||||
description: DAST_PROFILES_DESCRIPTION,
|
||||
configurationText: DAST_PROFILES_CONFIG_TEXT,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: DEPENDENCY_SCANNING_NAME,
|
||||
description: DEPENDENCY_SCANNING_DESCRIPTION,
|
||||
helpPath: DEPENDENCY_SCANNING_HELP_PATH,
|
||||
configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_DEPENDENCY_SCANNING,
|
||||
},
|
||||
{
|
||||
name: CONTAINER_SCANNING_NAME,
|
||||
description: CONTAINER_SCANNING_DESCRIPTION,
|
||||
helpPath: CONTAINER_SCANNING_HELP_PATH,
|
||||
configurationHelpPath: CONTAINER_SCANNING_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_CONTAINER_SCANNING,
|
||||
},
|
||||
{
|
||||
name: SECRET_DETECTION_NAME,
|
||||
description: SECRET_DETECTION_DESCRIPTION,
|
||||
helpPath: SECRET_DETECTION_HELP_PATH,
|
||||
configurationHelpPath: SECRET_DETECTION_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_SECRET_DETECTION,
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
name: API_FUZZING_NAME,
|
||||
description: API_FUZZING_DESCRIPTION,
|
||||
helpPath: API_FUZZING_HELP_PATH,
|
||||
type: REPORT_TYPE_API_FUZZING,
|
||||
},
|
||||
{
|
||||
name: COVERAGE_FUZZING_NAME,
|
||||
description: COVERAGE_FUZZING_DESCRIPTION,
|
||||
helpPath: COVERAGE_FUZZING_HELP_PATH,
|
||||
type: REPORT_TYPE_COVERAGE_FUZZING,
|
||||
},
|
||||
];
|
||||
|
||||
export const complianceFeatures = [
|
||||
{
|
||||
name: LICENSE_COMPLIANCE_NAME,
|
||||
description: LICENSE_COMPLIANCE_DESCRIPTION,
|
||||
helpPath: LICENSE_COMPLIANCE_HELP_PATH,
|
||||
type: REPORT_TYPE_LICENSE_COMPLIANCE,
|
||||
},
|
||||
];
|
||||
|
||||
export const featureToMutationMap = {
|
||||
[REPORT_TYPE_SAST]: {
|
||||
mutationId: 'configureSast',
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<script>
|
||||
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
import FeatureCard from './feature_card.vue';
|
||||
|
||||
export const i18n = {
|
||||
compliance: s__('SecurityConfiguration|Compliance'),
|
||||
securityTesting: s__('SecurityConfiguration|Security testing'),
|
||||
securityTestingDescription: s__(
|
||||
`SecurityConfiguration|The status of the tools only applies to the
|
||||
default branch and is based on the %{linkStart}latest pipeline%{linkEnd}.
|
||||
Once you've enabled a scan for the default branch, any subsequent feature
|
||||
branch you create will include the scan.`,
|
||||
),
|
||||
securityConfiguration: __('Security Configuration'),
|
||||
};
|
||||
|
||||
export default {
|
||||
i18n,
|
||||
components: {
|
||||
GlTab,
|
||||
GlLink,
|
||||
GlTabs,
|
||||
GlSprintf,
|
||||
FeatureCard,
|
||||
},
|
||||
props: {
|
||||
augmentedSecurityFeatures: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
latestPipelinePath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article>
|
||||
<header>
|
||||
<h1 class="gl-font-size-h1">{{ $options.i18n.securityConfiguration }}</h1>
|
||||
</header>
|
||||
|
||||
<gl-tabs content-class="gl-pt-6">
|
||||
<gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<h2 class="gl-font-size-h2 gl-mt-0">{{ $options.i18n.securityTesting }}</h2>
|
||||
<p
|
||||
v-if="latestPipelinePath"
|
||||
data-testid="latest-pipeline-info"
|
||||
class="gl-line-height-20"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.securityTestingDescription">
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="latestPipelinePath">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<feature-card
|
||||
v-for="feature in augmentedSecurityFeatures"
|
||||
:key="feature.type"
|
||||
:feature="feature"
|
||||
class="gl-mb-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
</article>
|
||||
</template>
|
|
@ -2,6 +2,9 @@ import Vue from 'vue';
|
|||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import SecurityConfigurationApp from './components/app.vue';
|
||||
import { securityFeatures, complianceFeatures } from './components/constants';
|
||||
import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue';
|
||||
import { augmentFeatures } from './utils';
|
||||
|
||||
export const initStaticSecurityConfiguration = (el) => {
|
||||
if (!el) {
|
||||
|
@ -14,8 +17,32 @@ export const initStaticSecurityConfiguration = (el) => {
|
|||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
|
||||
const { projectPath, upgradePath } = el.dataset;
|
||||
const { projectPath, upgradePath, features, latestPipelinePath } = el.dataset;
|
||||
|
||||
if (gon.features.securityConfigurationRedesign) {
|
||||
const { augmentedSecurityFeatures } = augmentFeatures(
|
||||
securityFeatures,
|
||||
complianceFeatures,
|
||||
features ? JSON.parse(features) : [],
|
||||
);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
provide: {
|
||||
projectPath,
|
||||
upgradePath,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(RedesignedSecurityConfigurationApp, {
|
||||
props: {
|
||||
augmentedSecurityFeatures,
|
||||
latestPipelinePath,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
return new Vue({
|
||||
el,
|
||||
apolloProvider,
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => {
|
||||
const featuresByType = features.reduce((acc, feature) => {
|
||||
acc[feature.type] = feature;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const augmentFeature = (feature) => {
|
||||
const augmented = {
|
||||
...feature,
|
||||
...featuresByType[feature.type],
|
||||
};
|
||||
|
||||
if (augmented.secondary) {
|
||||
augmented.secondary = { ...augmented.secondary, ...featuresByType[feature.secondary.type] };
|
||||
}
|
||||
|
||||
return augmented;
|
||||
};
|
||||
|
||||
return {
|
||||
augmentedSecurityFeatures: securityFeatures.map((feature) => augmentFeature(feature)),
|
||||
augmentedComplianceFeatures: complianceFeatures.map((feature) => augmentFeature(feature)),
|
||||
};
|
||||
};
|
|
@ -435,9 +435,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
|
||||
<header
|
||||
class="gl-overflow-hidden gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100"
|
||||
>
|
||||
<header class="gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100">
|
||||
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
|
||||
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
|
||||
</mr-widget-alert-message>
|
||||
|
|
|
@ -7,6 +7,10 @@ module Projects
|
|||
|
||||
feature_category :static_application_security_testing
|
||||
|
||||
before_action only: [:show] do
|
||||
push_frontend_feature_flag(:security_configuration_redesign, project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def show
|
||||
render_403 unless can?(current_user, :read_security_configuration, project)
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module Security
|
|||
attr_reader :pipeline
|
||||
|
||||
def self.allowed_job_types
|
||||
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_management, :coverage_fuzzing]
|
||||
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_scanning, :coverage_fuzzing]
|
||||
raise NotImplementedError, 'allowed_job_types must be overwritten to return an array of job types'
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
= link_to _('Forgot your password?'), new_password_path(:user)
|
||||
%div
|
||||
- if captcha_enabled? || captcha_on_login_required?
|
||||
= recaptcha_tags
|
||||
= recaptcha_tags nonce: content_security_policy_nonce
|
||||
|
||||
.submit-container.move-submit-down
|
||||
= f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' }
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
.devise-errors
|
||||
= render 'devise/shared/error_messages', resource: resource
|
||||
- if Gitlab::CurrentSettings.invisible_captcha_enabled
|
||||
= invisible_captcha
|
||||
= invisible_captcha nonce: true
|
||||
.name.form-row
|
||||
.col.form-group
|
||||
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
|
||||
|
@ -59,7 +59,7 @@
|
|||
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
|
||||
%div
|
||||
- if show_recaptcha_sign_up?
|
||||
= recaptcha_tags
|
||||
= recaptcha_tags nonce: content_security_policy_nonce
|
||||
.submit-container
|
||||
= f.submit button_text, class: 'btn gl-button btn-confirm', data: { qa_selector: 'new_user_register_button' }
|
||||
= render 'devise/shared/terms_of_service_notice', button_text: button_text
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
- if captcha_required?
|
||||
.row.recaptcha
|
||||
.col-sm-4
|
||||
= recaptcha_tags
|
||||
= recaptcha_tags nonce: content_security_policy_nonce
|
||||
.row
|
||||
.col-sm-12
|
||||
= f.submit _('Create group'), class: "btn gl-button btn-confirm"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
= hidden_field(resource_name, field, value: value)
|
||||
= hidden_field_tag(:spam_log_id, spammable.spam_log.id)
|
||||
-# The reCAPTCHA response value will be returned in the 'g-recaptcha-response' field
|
||||
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback' unless Rails.env.test?
|
||||
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback', nonce: content_security_policy_nonce unless Rails.env.test?
|
||||
-# Fake the 'g-recaptcha-response' field in the test environment, so that the feature spec
|
||||
-# can get to the (mocked) SpamVerdictService check.
|
||||
= hidden_field_tag('g-recaptcha-response', 'abc123') if Rails.env.test?
|
||||
|
|
|
@ -40,7 +40,9 @@ module AuthorizedProjectUpdate
|
|||
private
|
||||
|
||||
def use_primary_database
|
||||
# no-op in CE, overriden in EE
|
||||
if ::Gitlab::Database::LoadBalancing.enable?
|
||||
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
|
||||
end
|
||||
end
|
||||
|
||||
def project_authorizations_needs_refresh?(user)
|
||||
|
@ -54,5 +56,3 @@ module AuthorizedProjectUpdate
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.prepend_mod_with('AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker')
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
- audit_reports
|
||||
- authentication_and_authorization
|
||||
- auto_devops
|
||||
- auto_portfolio_mgmt
|
||||
- backup_restore
|
||||
- boards
|
||||
- browser_performance
|
||||
- chatops
|
||||
- cloud_native_installation
|
||||
- cluster_cost_management
|
||||
|
@ -78,7 +76,6 @@
|
|||
- license
|
||||
- license_compliance
|
||||
- live_preview
|
||||
- load_testing
|
||||
- logging
|
||||
- memory
|
||||
- merge_trains
|
||||
|
@ -87,9 +84,11 @@
|
|||
- mobile_signing_deployment
|
||||
- navigation
|
||||
- omnibus_package
|
||||
- on_call_schedule_management
|
||||
- onboarding
|
||||
- package_registry
|
||||
- pages
|
||||
- performance_testing
|
||||
- pipeline_authoring
|
||||
- planning_analytics
|
||||
- privacy_control_center
|
||||
|
@ -111,6 +110,7 @@
|
|||
- self_monitoring
|
||||
- serverless
|
||||
- service_desk
|
||||
- sharding
|
||||
- snippets
|
||||
- source_code_management
|
||||
- static_application_security_testing
|
||||
|
@ -130,3 +130,4 @@
|
|||
- web_firewall
|
||||
- web_ide
|
||||
- wiki
|
||||
- workflow_automation
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: security_configuration_redesign
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62285
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331614
|
||||
milestone: '14.0'
|
||||
type: development
|
||||
group: group::static analysis
|
||||
default_enabled: false
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
MIGRATION = 'UpdateJiraTrackerDataDeploymentTypeBasedOnUrl'
|
||||
DELAY_INTERVAL = 2.minutes.to_i
|
||||
BATCH_SIZE = 2_500
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
say "Scheduling #{MIGRATION} jobs"
|
||||
queue_background_migration_jobs_by_range_at_intervals(
|
||||
define_batchable_model('jira_tracker_data'),
|
||||
MIGRATION,
|
||||
DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveSegmentSelectionsTable < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
drop_table :analytics_devops_adoption_segment_selections
|
||||
end
|
||||
|
||||
def down
|
||||
create_table :analytics_devops_adoption_segment_selections do |t|
|
||||
t.references :segment, index: { name: 'index_on_segment_selections_segment_id' }, null: false, foreign_key: { to_table: :analytics_devops_adoption_segments, on_delete: :cascade }
|
||||
t.bigint :group_id
|
||||
t.bigint :project_id
|
||||
t.index [:group_id, :segment_id], unique: true, name: 'index_on_segment_selections_group_id_segment_id'
|
||||
t.index [:project_id, :segment_id], unique: true, name: 'index_on_segment_selections_project_id_segment_id'
|
||||
|
||||
t.timestamps_with_timezone
|
||||
end
|
||||
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :projects, column: :project_id, on_delete: :cascade)
|
||||
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :namespaces, column: :group_id, on_delete: :cascade)
|
||||
add_check_constraint :analytics_devops_adoption_segment_selections, '(project_id != NULL AND group_id IS NULL) OR (group_id != NULL AND project_id IS NULL)', 'segment_selection_project_id_or_group_id_required'
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
0f6019cc094481cafbf0c9bd42f53ae09034ea87e3f360b02f9ec03192caab9d
|
|
@ -0,0 +1 @@
|
|||
ee76ee2e2515c06b09fca23a77bdfb9532fa5d80fc3d5aba44a80d123b74cfa9
|
|
@ -9110,25 +9110,6 @@ CREATE SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq
|
|||
|
||||
ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id;
|
||||
|
||||
CREATE TABLE analytics_devops_adoption_segment_selections (
|
||||
id bigint NOT NULL,
|
||||
segment_id bigint NOT NULL,
|
||||
group_id bigint,
|
||||
project_id bigint,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
CONSTRAINT segment_selection_project_id_or_group_id_required CHECK ((((project_id <> NULL::bigint) AND (group_id IS NULL)) OR ((group_id <> NULL::bigint) AND (project_id IS NULL))))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE analytics_devops_adoption_segment_selections_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE analytics_devops_adoption_segment_selections_id_seq OWNED BY analytics_devops_adoption_segment_selections.id;
|
||||
|
||||
CREATE TABLE analytics_devops_adoption_segments (
|
||||
id bigint NOT NULL,
|
||||
last_recorded_at timestamp with time zone,
|
||||
|
@ -19520,8 +19501,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages ALTER COLUMN id SET DE
|
|||
|
||||
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segment_selections_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_snapshots ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_snapshots_id_seq'::regclass);
|
||||
|
@ -20611,9 +20590,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages
|
|||
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
|
||||
ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
||||
ADD CONSTRAINT analytics_devops_adoption_segment_selections_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segments
|
||||
ADD CONSTRAINT analytics_devops_adoption_segments_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -23899,12 +23875,6 @@ CREATE INDEX index_on_projects_lower_path ON projects USING btree (lower((path):
|
|||
|
||||
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
|
||||
|
||||
CREATE UNIQUE INDEX index_on_segment_selections_group_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (group_id, segment_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_on_segment_selections_project_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (project_id, segment_id);
|
||||
|
||||
CREATE INDEX index_on_segment_selections_segment_id ON analytics_devops_adoption_segment_selections USING btree (segment_id);
|
||||
|
||||
CREATE INDEX index_on_snapshots_segment_id_end_time ON analytics_devops_adoption_snapshots USING btree (segment_id, end_time);
|
||||
|
||||
CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
|
||||
|
@ -25955,9 +25925,6 @@ ALTER TABLE ONLY project_group_links
|
|||
ALTER TABLE ONLY epics
|
||||
ADD CONSTRAINT fk_dccd3f98fc FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
||||
ADD CONSTRAINT fk_ded7fe0344 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY issues
|
||||
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
|
||||
|
||||
|
@ -26027,9 +25994,6 @@ ALTER TABLE ONLY vulnerability_external_issue_links
|
|||
ALTER TABLE ONLY epics
|
||||
ADD CONSTRAINT fk_f081aa4489 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
||||
ADD CONSTRAINT fk_f1472b95f3 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY boards
|
||||
ADD CONSTRAINT fk_f15266b5f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
@ -26120,9 +26084,6 @@ ALTER TABLE ONLY ip_restrictions
|
|||
ALTER TABLE ONLY terraform_state_versions
|
||||
ADD CONSTRAINT fk_rails_04f176e239 FOREIGN KEY (terraform_state_id) REFERENCES terraform_states(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
|
||||
ADD CONSTRAINT fk_rails_053f00a9da FOREIGN KEY (segment_id) REFERENCES analytics_devops_adoption_segments(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY ci_build_report_results
|
||||
ADD CONSTRAINT fk_rails_056d298d48 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -21,9 +21,8 @@ GitLab has several features based on receiving incoming emails:
|
|||
|
||||
## Requirements
|
||||
|
||||
It is **not** recommended to use an email address that receives any
|
||||
messages not intended for the GitLab instance. Any incoming emails not intended
|
||||
for GitLab receive a reject notice.
|
||||
We recommend using an email address that receives **only** messages that are intended for
|
||||
the GitLab instance. Any incoming emails not intended for GitLab receive a reject notice.
|
||||
|
||||
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
|
||||
email account. GitLab requires one of the following three strategies:
|
||||
|
@ -131,6 +130,9 @@ list.
|
|||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
|
||||
# Needed when enabling or disabling for the first time but not for password changes.
|
||||
# See https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23560#note_61966788
|
||||
sudo gitlab-ctl restart
|
||||
```
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ The following are the requirements for providing your own Redis instance:
|
|||
[requirements page](../../install/requirements.md).
|
||||
- Standalone Redis or Redis high availability with Sentinel are supported. Redis
|
||||
Cluster is not supported.
|
||||
- Managed Redis from cloud providers such as AWS ElastiCache will work. If these
|
||||
- Managed Redis from cloud providers such as AWS ElastiCache works fine. If these
|
||||
services support high availability, be sure it is **not** the Redis Cluster type.
|
||||
|
||||
Note the Redis node's IP address or hostname, port, and password (if required).
|
||||
|
@ -53,7 +53,7 @@ Note the Redis node's IP address or hostname, port, and password (if required).
|
|||
This is the documentation for configuring a scalable Redis setup when
|
||||
you have installed Redis all by yourself and not using the bundled one that
|
||||
comes with the Omnibus packages, although using the Omnibus GitLab packages is
|
||||
highly recommend as we optimize them specifically for GitLab, and we will take
|
||||
highly recommend as we optimize them specifically for GitLab, and we take
|
||||
care of upgrading Redis to the latest supported version.
|
||||
|
||||
Note also that you may elect to override all references to
|
||||
|
@ -76,7 +76,7 @@ requirements:
|
|||
(e.g., one from an internal network).
|
||||
- Since Redis 3.2, you must define a password to receive external connections
|
||||
(`requirepass`).
|
||||
- If you are using Redis with Sentinel, you will also need to define the same
|
||||
- If you are using Redis with Sentinel, you also need to define the same
|
||||
password for the replica password definition (`masterauth`) in the same instance.
|
||||
|
||||
In addition, read the prerequisites as described in the
|
||||
|
@ -176,7 +176,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
|
|||
sentinel monitor gitlab-redis 10.0.0.1 6379 2
|
||||
|
||||
## Define with `sentinel down-after-milliseconds` the time in `ms`
|
||||
## that an unresponsive server will be considered down.
|
||||
## that an unresponsive server is considered down.
|
||||
sentinel down-after-milliseconds gitlab-redis 10000
|
||||
|
||||
## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
|
||||
|
@ -197,7 +197,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
|
|||
##
|
||||
## * The maximum time a failover in progress waits for all the replicas to be
|
||||
## reconfigured as replicas of the new primary. However even after this time
|
||||
## the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
## the replicas are reconfigured by the Sentinels anyway, but not with
|
||||
## the exact parallel-syncs progression as specified.
|
||||
sentinel failover_timeout 30000
|
||||
```
|
||||
|
@ -249,7 +249,7 @@ In a real world usage, you would also set up firewall rules to prevent
|
|||
unauthorized access from other machines, and block traffic from the
|
||||
outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
|
||||
|
||||
For this example, **Sentinel 1** will be configured in the same machine as the
|
||||
For this example, **Sentinel 1** is configured in the same machine as the
|
||||
**Redis Primary**, **Sentinel 2** and **Sentinel 3** in the same machines as the
|
||||
**Replica 1** and **Replica 2** respectively.
|
||||
|
||||
|
@ -261,11 +261,11 @@ Here is a list and description of each **machine** and the assigned **IP**:
|
|||
- `10.0.0.4`: GitLab application
|
||||
|
||||
Please note that after the initial configuration, if a failover is initiated
|
||||
by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
|
||||
will change permanently (including in `redis.conf`) from one node to the other,
|
||||
by the Sentinel nodes, the Redis nodes are reconfigured and the **Primary**
|
||||
changes permanently (including in `redis.conf`) from one node to the other,
|
||||
until a new failover is initiated again.
|
||||
|
||||
The same thing will happen with `sentinel.conf` that will be overridden after the
|
||||
The same thing happens with `sentinel.conf` that is overridden after the
|
||||
initial execution, after any new sentinel node starts watching the **Primary**,
|
||||
or a failover promotes a different **Primary** node.
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Create an A/B test with `Experimentation Module`
|
||||
|
||||
NOTE:
|
||||
We recommend using [GLEX](gitlab_experiment.md) for new experiments.
|
||||
|
||||
## Implement the experiment
|
||||
|
||||
1. Add the experiment to the `Gitlab::Experimentation::EXPERIMENTS` hash in
|
||||
|
|
|
@ -46,16 +46,10 @@ One is built into GitLab directly and has been around for a while (this is calle
|
|||
to as `Gitlab::Experiment` -- GLEX for short.
|
||||
|
||||
Both approaches use [experiment](../feature_flags/index.md#experiment-type)
|
||||
feature flags, and there is currently no strong suggestion to use one over the other.
|
||||
feature flags. We recommend using GLEX rather than `Experimentation Module` for new experiments.
|
||||
|
||||
| Feature | `Experimentation Module` | GLEX |
|
||||
| -------------------- |------------------------- | ---- |
|
||||
| Record user grouping | Yes | No |
|
||||
| Uses feature flags | Yes | Yes |
|
||||
| Multivariate (A/B/n) | No | Yes |
|
||||
|
||||
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
|
||||
- [Implementing an A/B/n experiment using GLEX](gitlab_experiment.md)
|
||||
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
|
||||
|
||||
Historical Context: `Experimentation Module` was built iteratively with the needs that
|
||||
appeared while implementing Growth sub-department experiments, while GLEX was built
|
||||
|
|
|
@ -105,6 +105,12 @@ Default client accepts two parameters: `resolvers` and `config`.
|
|||
- `baseUrl` allows us to pass a URL for GraphQL endpoint different from our main endpoint (for example, `${gon.relative_url_root}/api/graphql`)
|
||||
- `assumeImmutableResults` (set to `false` by default) - this setting, when set to `true`, assumes that every single operation on updating Apollo Cache is immutable. It also sets `freezeResults` to `true`, so any attempt on mutating Apollo Cache throws a console warning in development environment. Please ensure you're following the immutability pattern on cache update operations before setting this option to `true`.
|
||||
- `fetchPolicy` determines how you want your component to interact with the Apollo cache. Defaults to "cache-first".
|
||||
|
||||
### Multiple client queries for the same object
|
||||
|
||||
If you are make multiple queries to the same Apollo client object you might encounter the following error: "Store error: the application attempted to write an object with no provided ID but the store already contains an ID of SomeEntity". [This error only should occur when you have made a query with an ID field for a portion, then made another that returns what would be the same object, but is missing the ID field.](https://github.com/apollographql/apollo-client/issues/2510#issue-271829009)
|
||||
|
||||
Please note this is being tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326101) and the documentation will be updated when this issue is resolved.
|
||||
|
||||
## GraphQL Queries
|
||||
|
||||
|
|
|
@ -132,6 +132,9 @@ the QA smoke suite.
|
|||
|
||||
You can also manually start the `review-qa-all`: it runs the full QA suite.
|
||||
|
||||
After the end-to-end test runs have finished, [Allure reports](https://github.com/allure-framework/allure2) are generated and published by
|
||||
the `allure-report-qa-smoke` and `allure-report-qa-all` jobs. A comment with links to the reports are added to the merge request.
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage, the
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable Style/Documentation
|
||||
class Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl
|
||||
# rubocop: disable Gitlab/NamespacedClass
|
||||
class JiraTrackerData < ActiveRecord::Base
|
||||
self.table_name = "jira_tracker_data"
|
||||
self.inheritance_column = :_type_disabled
|
||||
|
||||
include ::Integrations::BaseDataFields
|
||||
attr_encrypted :url, encryption_options
|
||||
attr_encrypted :api_url, encryption_options
|
||||
|
||||
enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment
|
||||
end
|
||||
# rubocop: enable Gitlab/NamespacedClass
|
||||
|
||||
# https://rubular.com/r/uwgK7k9KH23efa
|
||||
JIRA_CLOUD_REGEX = %r{^https?://[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.atlassian\.net$}ix.freeze
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def perform(start_id, end_id)
|
||||
trackers_data = JiraTrackerData
|
||||
.where(deployment_type: 'unknown')
|
||||
.where(id: start_id..end_id)
|
||||
|
||||
cloud, server = trackers_data.partition { |tracker_data| tracker_data.url.match?(JIRA_CLOUD_REGEX) }
|
||||
|
||||
cloud_mappings = cloud.each_with_object({}) do |tracker_data, hash|
|
||||
hash[tracker_data] = { deployment_type: 2 }
|
||||
end
|
||||
|
||||
server_mapppings = server.each_with_object({}) do |tracker_data, hash|
|
||||
hash[tracker_data] = { deployment_type: 1 }
|
||||
end
|
||||
|
||||
mappings = cloud_mappings.merge(server_mapppings)
|
||||
|
||||
::Gitlab::Database::BulkUpdate.execute(%i[deployment_type], mappings)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
|
@ -77,7 +77,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def set_section_duration(duration)
|
||||
@section_duration = Time.at(duration.to_i).strftime('%M:%S')
|
||||
@section_duration = Time.at(duration.to_i).utc.strftime('%M:%S')
|
||||
end
|
||||
|
||||
def flush_current_segment!
|
||||
|
|
|
@ -18,11 +18,11 @@ module Gitlab
|
|||
'font_src' => "'self'",
|
||||
'form_action' => "'self' https: http:",
|
||||
'frame_ancestors' => "'self'",
|
||||
'frame_src' => "'self' https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
|
||||
'frame_src' => "'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
|
||||
'img_src' => "'self' data: blob: http: https:",
|
||||
'manifest_src' => "'self'",
|
||||
'media_src' => "'self'",
|
||||
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com",
|
||||
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
|
||||
'style_src' => "'self' 'unsafe-inline'",
|
||||
'worker_src' => "'self'",
|
||||
'object_src' => "'none'",
|
||||
|
|
|
@ -28955,6 +28955,9 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Configuration guide"
|
||||
msgstr ""
|
||||
|
||||
|
@ -28994,6 +28997,9 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Manage"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Manage scans"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|More information"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29009,12 +29015,18 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Security Control"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Security testing"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Testing & Compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|The status of the tools only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Using custom settings. You won't receive automatic updates on this variable. %{anchorStart}Restore to default%{anchorEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -28,19 +28,19 @@ RSpec.describe Security::SecurityJobsFinder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with combination of security jobs and license management jobs' do
|
||||
context 'with combination of security jobs and license scanning jobs' do
|
||||
let!(:sast_build) { create(:ci_build, :sast, pipeline: pipeline) }
|
||||
let!(:container_scanning_build) { create(:ci_build, :container_scanning, pipeline: pipeline) }
|
||||
let!(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) }
|
||||
let!(:secret_detection_build) { create(:ci_build, :secret_detection, pipeline: pipeline) }
|
||||
let!(:license_management_build) { create(:ci_build, :license_management, pipeline: pipeline) }
|
||||
let!(:license_scanning_build) { create(:ci_build, :license_scanning, pipeline: pipeline) }
|
||||
|
||||
it 'returns only the security jobs' do
|
||||
is_expected.to include(sast_build)
|
||||
is_expected.to include(container_scanning_build)
|
||||
is_expected.to include(dast_build)
|
||||
is_expected.to include(secret_detection_build)
|
||||
is_expected.not_to include(license_management_build)
|
||||
is_expected.not_to include(license_scanning_build)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { GlTab, GlTabs } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import {
|
||||
SAST_NAME,
|
||||
SAST_SHORT_NAME,
|
||||
SAST_DESCRIPTION,
|
||||
SAST_HELP_PATH,
|
||||
SAST_CONFIG_HELP_PATH,
|
||||
} from '~/security_configuration/components/constants';
|
||||
import FeatureCard from '~/security_configuration/components/feature_card.vue';
|
||||
import RedesignedSecurityConfigurationApp, {
|
||||
i18n,
|
||||
} from '~/security_configuration/components/redesigned_app.vue';
|
||||
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants';
|
||||
|
||||
describe('NewApp component', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (propsData) => {
|
||||
wrapper = extendedWrapper(
|
||||
mount(RedesignedSecurityConfigurationApp, {
|
||||
propsData,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const findMainHeading = () => wrapper.find('h1');
|
||||
const findSubHeading = () => wrapper.find('h2');
|
||||
const findTab = () => wrapper.findComponent(GlTab);
|
||||
const findTabs = () => wrapper.findAllComponents(GlTabs);
|
||||
const findByTestId = (id) => wrapper.findByTestId(id);
|
||||
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
|
||||
|
||||
const securityFeaturesMock = [
|
||||
{
|
||||
name: SAST_NAME,
|
||||
shortName: SAST_SHORT_NAME,
|
||||
description: SAST_DESCRIPTION,
|
||||
helpPath: SAST_HELP_PATH,
|
||||
configurationHelpPath: SAST_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_SAST,
|
||||
available: true,
|
||||
},
|
||||
];
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('basic structure', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
augmentedSecurityFeatures: securityFeaturesMock,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders main-heading with correct text', () => {
|
||||
const mainHeading = findMainHeading();
|
||||
expect(mainHeading).toExist();
|
||||
expect(mainHeading.text()).toContain('Security Configuration');
|
||||
});
|
||||
|
||||
it('renders GlTab Component ', () => {
|
||||
expect(findTab()).toExist();
|
||||
});
|
||||
|
||||
it('renders right amount of tabs with correct title ', () => {
|
||||
expect(findTabs().length).toEqual(1);
|
||||
});
|
||||
|
||||
it('renders security-testing tab', () => {
|
||||
expect(findByTestId('security-testing-tab')).toExist();
|
||||
});
|
||||
|
||||
it('renders sub-heading with correct text', () => {
|
||||
const subHeading = findSubHeading();
|
||||
expect(subHeading).toExist();
|
||||
expect(subHeading.text()).toContain(i18n.securityTesting);
|
||||
});
|
||||
|
||||
it('renders right amount of feature cards for given props with correct props', () => {
|
||||
const cards = findFeatureCards();
|
||||
expect(cards.length).toEqual(1);
|
||||
expect(cards.at(0).props()).toEqual({ feature: securityFeaturesMock[0] });
|
||||
});
|
||||
|
||||
it('should not show latest pipeline link when latestPipelinePath is not defined', () => {
|
||||
expect(findByTestId('latest-pipeline-info').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when given latestPipelinePath props', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
augmentedSecurityFeatures: securityFeaturesMock,
|
||||
latestPipelinePath: 'test/path',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show latest pipeline info with correct link when latestPipelinePath is defined', () => {
|
||||
expect(findByTestId('latest-pipeline-info').exists()).toBe(true);
|
||||
expect(findByTestId('latest-pipeline-info').text()).toMatchInterpolatedText(
|
||||
i18n.securityTestingDescription,
|
||||
);
|
||||
expect(findByTestId('latest-pipeline-info').find('a').attributes('href')).toBe('test/path');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
import { augmentFeatures } from '~/security_configuration/utils';
|
||||
|
||||
const mockSecurityFeatures = [
|
||||
{
|
||||
name: 'SAST',
|
||||
type: 'SAST',
|
||||
},
|
||||
];
|
||||
|
||||
const mockComplianceFeatures = [
|
||||
{
|
||||
name: 'LICENSE_COMPLIANCE',
|
||||
type: 'LICENSE_COMPLIANCE',
|
||||
},
|
||||
];
|
||||
|
||||
const mockFeaturesWithSecondary = [
|
||||
{
|
||||
name: 'DAST',
|
||||
type: 'DAST',
|
||||
secondary: {
|
||||
type: 'DAST PROFILES',
|
||||
name: 'DAST PROFILES',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const mockInvalidCustomFeature = [
|
||||
{
|
||||
foo: 'bar',
|
||||
},
|
||||
];
|
||||
|
||||
const mockValidCustomFeature = [
|
||||
{
|
||||
name: 'SAST',
|
||||
type: 'SAST',
|
||||
customfield: 'customvalue',
|
||||
},
|
||||
];
|
||||
|
||||
const expectedOutputDefault = {
|
||||
augmentedSecurityFeatures: mockSecurityFeatures,
|
||||
augmentedComplianceFeatures: mockComplianceFeatures,
|
||||
};
|
||||
|
||||
const expectedOutputSecondary = {
|
||||
augmentedSecurityFeatures: mockSecurityFeatures,
|
||||
augmentedComplianceFeatures: mockFeaturesWithSecondary,
|
||||
};
|
||||
|
||||
const expectedOutputCustomFeature = {
|
||||
augmentedSecurityFeatures: mockValidCustomFeature,
|
||||
augmentedComplianceFeatures: mockComplianceFeatures,
|
||||
};
|
||||
|
||||
describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => {
|
||||
it('given an empty array', () => {
|
||||
expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual(
|
||||
expectedOutputDefault,
|
||||
);
|
||||
});
|
||||
|
||||
it('given an invalid populated array', () => {
|
||||
expect(
|
||||
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature),
|
||||
).toEqual(expectedOutputDefault);
|
||||
});
|
||||
|
||||
it('features have secondary key', () => {
|
||||
expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual(
|
||||
expectedOutputSecondary,
|
||||
);
|
||||
});
|
||||
|
||||
it('given a valid populated array', () => {
|
||||
expect(
|
||||
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature),
|
||||
).toEqual(expectedOutputCustomFeature);
|
||||
});
|
||||
});
|
|
@ -66,14 +66,16 @@ RSpec.describe Mutations::Ci::Runner::Update do
|
|||
|
||||
context 'with valid arguments' do
|
||||
it 'updates runner with correct values' do
|
||||
expected_attributes = mutation_params.except(:id)
|
||||
expected_attributes = mutation_params.except(:id, :tag_list)
|
||||
|
||||
subject
|
||||
|
||||
expect(subject[:errors]).to be_empty
|
||||
expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
|
||||
expect(subject[:runner]).to have_attributes(expected_attributes)
|
||||
expect(subject[:runner].tag_list).to contain_exactly(*mutation_params[:tag_list])
|
||||
expect(runner.reload).to have_attributes(expected_attributes)
|
||||
expect(runner.tag_list).to contain_exactly(*mutation_params[:tag_list])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl do
|
||||
let(:services_table) { table(:services) }
|
||||
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
|
||||
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
|
||||
|
||||
before do
|
||||
jira_tracker_data = Class.new(ApplicationRecord) do
|
||||
self.table_name = 'jira_tracker_data'
|
||||
|
||||
def self.encryption_options
|
||||
{
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv,
|
||||
algorithm: 'aes-256-gcm'
|
||||
}
|
||||
end
|
||||
|
||||
attr_encrypted :url, encryption_options
|
||||
attr_encrypted :api_url, encryption_options
|
||||
attr_encrypted :username, encryption_options
|
||||
attr_encrypted :password, encryption_options
|
||||
end
|
||||
|
||||
stub_const('JiraTrackerData', jira_tracker_data)
|
||||
end
|
||||
|
||||
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
|
||||
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
|
||||
|
||||
subject { described_class.new.perform(tracker_data_cloud.id, tracker_data_server.id) }
|
||||
|
||||
it "changes unknown deployment_types based on URL" do
|
||||
expect(JiraTrackerData.pluck(:deployment_type)).to eq([0, 0])
|
||||
|
||||
subject
|
||||
|
||||
expect(JiraTrackerData.pluck(:deployment_type)).to eq([2, 1])
|
||||
end
|
||||
end
|
|
@ -76,10 +76,30 @@ RSpec.describe Gitlab::Ci::Ansi2json::Line do
|
|||
end
|
||||
|
||||
describe '#set_section_duration' do
|
||||
it 'sets and formats the section_duration' do
|
||||
subject.set_section_duration(75)
|
||||
shared_examples 'set_section_duration' do
|
||||
it 'sets and formats the section_duration' do
|
||||
subject.set_section_duration(75)
|
||||
|
||||
expect(subject.section_duration).to eq('01:15')
|
||||
expect(subject.section_duration).to eq('01:15')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with default timezone' do
|
||||
it_behaves_like 'set_section_duration'
|
||||
end
|
||||
|
||||
context 'with a timezone carrying minutes offset' do
|
||||
before do
|
||||
# The actual call by does use Time.at(...).utc that the following
|
||||
# rubocop rule (Rails/TimeZone) suggests, but for this specific
|
||||
# test's purposes we needed to mock at the Time.at call point.
|
||||
|
||||
# rubocop:disable Rails/TimeZone
|
||||
allow(Time).to receive(:at).with(75).and_return(Time.at(75, in: '+05:30'))
|
||||
# rubocop:enable Rails/TimeZone
|
||||
end
|
||||
|
||||
it_behaves_like 'set_section_duration'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
|
|||
settings = described_class.default_settings_hash
|
||||
directives = settings['directives']
|
||||
|
||||
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com https://example.com")
|
||||
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
|
||||
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require Rails.root.join('db', 'post_migrate', '20210421163509_schedule_update_jira_tracker_data_deployment_type_based_on_url.rb')
|
||||
|
||||
RSpec.describe ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl, :migration do
|
||||
let(:services_table) { table(:services) }
|
||||
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
|
||||
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
|
||||
|
||||
before do
|
||||
jira_tracker_data = Class.new(ApplicationRecord) do
|
||||
self.table_name = 'jira_tracker_data'
|
||||
|
||||
def self.encryption_options
|
||||
{
|
||||
key: Settings.attr_encrypted_db_key_base_32,
|
||||
encode: true,
|
||||
mode: :per_attribute_iv,
|
||||
algorithm: 'aes-256-gcm'
|
||||
}
|
||||
end
|
||||
|
||||
attr_encrypted :url, encryption_options
|
||||
attr_encrypted :api_url, encryption_options
|
||||
attr_encrypted :username, encryption_options
|
||||
attr_encrypted :password, encryption_options
|
||||
end
|
||||
|
||||
stub_const('JiraTrackerData', jira_tracker_data)
|
||||
stub_const("#{described_class}::BATCH_SIZE", 1)
|
||||
end
|
||||
|
||||
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
|
||||
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
|
||||
|
||||
around do |example|
|
||||
freeze_time { Sidekiq::Testing.fake! { example.run } }
|
||||
end
|
||||
|
||||
it 'schedules background migration' do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_cloud.id, tracker_data_cloud.id)
|
||||
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_server.id, tracker_data_server.id)
|
||||
end
|
||||
end
|
|
@ -3,7 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
|
||||
let(:project) { create(:project) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:user) { project.namespace.owner }
|
||||
let(:start_user_id) { user.id }
|
||||
let(:end_user_id) { start_user_id }
|
||||
|
@ -64,6 +65,18 @@ RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
|
|||
|
||||
execute_worker
|
||||
end
|
||||
|
||||
context 'when load balancing is enabled' do
|
||||
before do
|
||||
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
|
||||
end
|
||||
|
||||
it 'reads from the primary database' do
|
||||
expect(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary!)
|
||||
|
||||
execute_worker
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue