Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-09-20 09:11:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-09-20 09:11:22 +0300
commit13ad005a25f163520ff94b90cdd53495c5a0b5c4 (patch)
tree5b2fb4262633c200a8e701de2d1fa97105641ed2
parent9a61b4604efea1c8e57f4c90addbc94ecbe874de (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/style/redundant_freeze.yml51
-rw-r--r--app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_empty_state.vue1
-rw-r--r--app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue26
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js26
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue32
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue62
-rw-r--r--app/graphql/mutations/ci/project_ci_cd_settings_update.rb13
-rw-r--r--app/graphql/mutations/users/set_namespace_commit_email.rb2
-rw-r--r--app/graphql/types/ci/ci_cd_setting_type.rb3
-rw-r--r--app/graphql/types/namespace_type.rb2
-rw-r--r--app/models/project_ci_cd_setting.rb1
-rw-r--r--app/models/project_import_state.rb7
-rw-r--r--app/models/sent_notification.rb4
-rw-r--r--app/policies/group_policy.rb4
-rw-r--r--app/policies/namespaces/group_project_namespace_shared_policy.rb1
-rw-r--r--app/policies/namespaces/user_namespace_policy.rb1
-rw-r--r--app/services/import/github/cancel_project_import_service.rb6
-rw-r--r--app/services/projects/group_links/create_service.rb2
-rw-r--r--app/services/users/set_namespace_commit_email_service.rb2
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml2
-rw-r--r--app/views/projects/pipelines/new.html.haml1
-rw-r--r--app/workers/concerns/gitlab/github_import/object_importer.rb2
-rw-r--r--app/workers/concerns/gitlab/github_import/stage_methods.rb2
-rw-r--r--config/feature_flags/development/issue_assignees_widget.yml2
-rw-r--r--config/feature_flags/development/merge_trains_skip_train.yml8
-rw-r--r--db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml6
-rw-r--r--db/migrate/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion.rb36
-rw-r--r--db/migrate/20230906204935_restart_self_hosted_sent_notifications_backfill.rb56
-rw-r--r--db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb13
-rw-r--r--db/post_migrate/20230912105945_queue_backfill_finding_id_in_vulnerabilities.rb25
-rw-r--r--db/schema_migrations/202309062049341
-rw-r--r--db/schema_migrations/202309062049351
-rw-r--r--db/schema_migrations/202309121059451
-rw-r--r--db/schema_migrations/202309181941531
-rw-r--r--db/structure.sql3
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/debian_group_packages.rb2
-rwxr-xr-xlib/api/go_proxy.rb2
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/v3/github.rb2
-rw-r--r--lib/api/validations/validators/git_ref.rb2
-rw-r--r--lib/atlassian/jira_connect/jwt/asymmetric.rb2
-rw-r--r--lib/banzai/color_parser.rb20
-rw-r--r--lib/banzai/filter/ascii_doc_sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/attributes_filter.rb6
-rw-r--r--lib/banzai/filter/autolink_filter.rb2
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb2
-rw-r--r--lib/banzai/filter/footnote_filter.rb4
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb4
-rw-r--r--lib/banzai/filter/markdown_post_escape_filter.rb6
-rw-r--r--lib/banzai/filter/markdown_pre_escape_filter.rb2
-rw-r--r--lib/banzai/filter/references/abstract_reference_filter.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb2
-rw-r--r--lib/banzai/filter/task_list_filter.rb4
-rw-r--r--lib/bulk_imports/common/pipelines/uploads_pipeline.rb2
-rw-r--r--lib/bulk_imports/file_downloads/filename_fetch.rb2
-rw-r--r--lib/error_tracking/sentry_client/pagination_parser.rb2
-rw-r--r--lib/expand_variables.rb2
-rw-r--r--lib/feature/definition.rb2
-rw-r--r--lib/gitaly/server.rb2
-rw-r--r--lib/gitlab.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities.rb28
-rw-r--r--lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb4
-rw-r--r--lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb2
-rw-r--r--lib/gitlab/changelog/generator.rb2
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb4
-rw-r--r--lib/gitlab/ci/config/entry/artifacts.rb2
-rw-r--r--lib/gitlab/ci/config/external/file/base.rb2
-rw-r--r--lib/gitlab/ci/parsers/test/junit.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/skip.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/and.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/equals.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/matches.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/null.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/or.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/string.rb2
-rw-r--r--lib/gitlab/ci/pipeline/expression/lexeme/variable.rb2
-rw-r--r--lib/gitlab/ci/trace/section_parser.rb2
-rw-r--r--lib/gitlab/ci/variables/collection/item.rb2
-rw-r--r--lib/gitlab/cleanup/project_uploads.rb2
-rw-r--r--lib/gitlab/color.rb2
-rw-r--r--lib/gitlab/config/loader/multi_doc_yaml.rb2
-rw-r--r--lib/gitlab/database/partitioning.rb10
-rw-r--r--lib/gitlab/database/partitioning/partition_manager.rb43
-rw-r--r--lib/gitlab/github_import/object_counter.rb2
-rw-r--r--locale/gitlab.pot26
-rw-r--r--package.json2
-rw-r--r--spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js17
-rw-r--r--spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js92
-rw-r--r--spec/graphql/mutations/users/set_namespace_commit_email_spec.rb2
-rw-r--r--spec/graphql/types/namespace_type_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities_spec.rb133
-rw-r--r--spec/lib/gitlab/database/partitioning/partition_manager_spec.rb35
-rw-r--r--spec/lib/gitlab/database/partitioning_spec.rb51
-rw-r--r--spec/migrations/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion_spec.rb144
-rw-r--r--spec/migrations/20230906204935_restart_self_hosted_sent_notifications_backfill_spec.rb162
-rw-r--r--spec/migrations/20230912105945_queue_backfill_finding_id_in_vulnerabilities_spec.rb26
-rw-r--r--spec/models/project_import_state_spec.rb8
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/policies/group_policy_spec.rb2
-rw-r--r--spec/policies/namespaces/user_namespace_policy_spec.rb2
-rw-r--r--spec/requests/api/project_attributes.yml1
-rw-r--r--spec/serializers/import/github_realtime_repo_entity_spec.rb4
-rw-r--r--spec/serializers/import/github_realtime_repo_serializer_spec.rb2
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb2
-rw-r--r--yarn.lock8
113 files changed, 940 insertions, 414 deletions
diff --git a/.rubocop_todo/style/redundant_freeze.yml b/.rubocop_todo/style/redundant_freeze.yml
index 12c9200bf54..a575ca7b804 100644
--- a/.rubocop_todo/style/redundant_freeze.yml
+++ b/.rubocop_todo/style/redundant_freeze.yml
@@ -2,57 +2,6 @@
# Cop supports --autocorrect.
Style/RedundantFreeze:
Exclude:
- - 'lib/api/api.rb'
- - 'lib/api/debian_group_packages.rb'
- - 'lib/api/go_proxy.rb'
- - 'lib/api/helpers.rb'
- - 'lib/api/v3/github.rb'
- - 'lib/api/validations/validators/git_ref.rb'
- - 'lib/atlassian/jira_connect/jwt/asymmetric.rb'
- - 'lib/banzai/color_parser.rb'
- - 'lib/banzai/filter/ascii_doc_sanitization_filter.rb'
- - 'lib/banzai/filter/attributes_filter.rb'
- - 'lib/banzai/filter/autolink_filter.rb'
- - 'lib/banzai/filter/blockquote_fence_filter.rb'
- - 'lib/banzai/filter/footnote_filter.rb'
- - 'lib/banzai/filter/gollum_tags_filter.rb'
- - 'lib/banzai/filter/markdown_post_escape_filter.rb'
- - 'lib/banzai/filter/markdown_pre_escape_filter.rb'
- - 'lib/banzai/filter/references/abstract_reference_filter.rb'
- - 'lib/banzai/filter/sanitization_filter.rb'
- - 'lib/banzai/filter/task_list_filter.rb'
- - 'lib/bulk_imports/common/pipelines/uploads_pipeline.rb'
- - 'lib/bulk_imports/file_downloads/filename_fetch.rb'
- - 'lib/error_tracking/sentry_client/pagination_parser.rb'
- - 'lib/expand_variables.rb'
- - 'lib/feature/definition.rb'
- - 'lib/gitaly/server.rb'
- - 'lib/gitlab.rb'
- - 'lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb'
- - 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- - 'lib/gitlab/changelog/generator.rb'
- - 'lib/gitlab/ci/build/artifacts/metadata.rb'
- - 'lib/gitlab/ci/config/entry/artifacts.rb'
- - 'lib/gitlab/ci/config/external/file/base.rb'
- - 'lib/gitlab/ci/parsers/test/junit.rb'
- - 'lib/gitlab/ci/pipeline/chain/skip.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/and.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/equals.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/matches.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/null.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/or.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/string.rb'
- - 'lib/gitlab/ci/pipeline/expression/lexeme/variable.rb'
- - 'lib/gitlab/ci/trace/section_parser.rb'
- - 'lib/gitlab/ci/variables/collection/item.rb'
- - 'lib/gitlab/cleanup/project_uploads.rb'
- - 'lib/gitlab/color.rb'
- - 'lib/gitlab/config/loader/multi_doc_yaml.rb'
- 'lib/gitlab/database/background_migration/batch_optimizer.rb'
- 'lib/gitlab/database/load_balancing/service_discovery.rb'
- 'lib/gitlab/database/migrations/runner.rb'
diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_empty_state.vue b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_empty_state.vue
index fbdb60f61f1..f701bedc74d 100644
--- a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_empty_state.vue
+++ b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_empty_state.vue
@@ -41,6 +41,7 @@ export default {
<template>
<gl-empty-state
:svg-path="$options.SCHEDULE_MD_SVG_URL"
+ :svg-height="150"
:primary-button-text="$options.i18n.createNew"
:primary-button-link="newSchedulePath"
>
diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
index 0c3ede47015..cd1d9a97ef3 100644
--- a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
+++ b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue
@@ -370,7 +370,7 @@ export default {
/>
</gl-form-group>
<!--Variable List-->
- <gl-form-group class="gl-mb-2" :label="$options.i18n.variables">
+ <gl-form-group class="gl-mb-0" :label="$options.i18n.variables">
<div
v-for="(variable, index) in variables"
:key="`var-${index}`"
@@ -456,13 +456,23 @@ export default {
<gl-form-checkbox id="schedule-active" v-model="activated" class="gl-mb-3">
{{ $options.i18n.activated }}
</gl-form-checkbox>
-
- <gl-button variant="confirm" data-testid="schedule-submit-button" @click="scheduleHandler">
- {{ buttonText }}
- </gl-button>
- <gl-button :href="schedulesPath" data-testid="schedule-cancel-button">
- {{ $options.i18n.cancel }}
- </gl-button>
+ <div class="gl-display-flex gl-gap-3 gl-flex-wrap">
+ <gl-button
+ variant="confirm"
+ data-testid="schedule-submit-button"
+ class="gl-w-full gl-sm-w-auto"
+ @click="scheduleHandler"
+ >
+ {{ buttonText }}
+ </gl-button>
+ <gl-button
+ :href="schedulesPath"
+ data-testid="schedule-cancel-button"
+ class="gl-w-full gl-sm-w-auto"
+ >
+ {{ $options.i18n.cancel }}
+ </gl-button>
+ </div>
</gl-form>
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
index ec183edda53..0eff9110412 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
@@ -1,29 +1,3 @@
-import Vue from 'vue';
import initPipelineSchedulesApp from '~/ci/pipeline_schedules/mount_pipeline_schedules_app';
-import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
-
-function initPipelineSchedulesCallout() {
- const el = document.getElementById('pipeline-schedules-callout');
-
- if (!el) {
- return;
- }
-
- const { docsUrl, illustrationUrl } = el.dataset;
-
- // eslint-disable-next-line no-new
- new Vue({
- el,
- name: 'PipelineSchedulesCalloutRoot',
- provide: {
- docsUrl,
- illustrationUrl,
- },
- render(createElement) {
- return createElement(PipelineSchedulesCallout);
- },
- });
-}
initPipelineSchedulesApp();
-initPipelineSchedulesCallout();
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 642fd56eab1..9c4582ece21 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -1,12 +1,5 @@
<script>
-import {
- GlFormRadio,
- GlFormRadioGroup,
- GlIcon,
- GlLink,
- GlSprintf,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlFormRadio, GlFormRadioGroup, GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui';
import { getWeekdayNames } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -23,7 +16,6 @@ export default {
GlFormRadioGroup,
GlIcon,
GlLink,
- GlSprintf,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -97,8 +89,7 @@ export default {
},
{
value: KEY_CUSTOM,
- text: s__('PipelineScheduleIntervalPattern|Custom (%{linkStart}Learn more%{linkEnd}.)'),
- link: this.cronSyntaxUrl,
+ text: s__('PipelineScheduleIntervalPattern|Custom'),
},
];
},
@@ -155,6 +146,10 @@ export default {
return value === KEY_CUSTOM && this.dailyLimit;
},
},
+ i18n: {
+ learnCronSyntax: s__('PipelineScheduleIntervalPattern|Set a custom interval with Cron syntax.'),
+ cronSyntaxLink: s__('PipelineScheduleIntervalPattern|What is Cron syntax?'),
+ },
};
</script>
@@ -167,19 +162,14 @@ export default {
:value="option.value"
:data-testid="option.value"
>
- <gl-sprintf v-if="option.link" :message="option.text">
- <template #link="{ content }">
- <gl-link :href="option.link" target="_blank" class="gl-font-sm">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
-
- <template v-else>{{ option.text }}</template>
+ {{ option.text }}
<gl-icon
v-if="showDailyLimitMessage(option)"
v-gl-tooltip.hover
name="question-o"
:title="scheduleDailyLimitMsg"
+ data-testid="daily-limit"
/>
</gl-form-radio>
</gl-form-radio-group>
@@ -193,5 +183,11 @@ export default {
required="true"
@input="onCustomInput"
/>
+ <p class="gl-mt-1 gl-mb-0 gl-text-secondary">
+ {{ $options.i18n.learnCronSyntax }}
+ <gl-link :href="cronSyntaxUrl" target="_blank">
+ {{ $options.i18n.cronSyntaxLink }}
+ </gl-link>
+ </p>
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
deleted file mode 100644
index b3ad50f395b..00000000000
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import Vue from 'vue';
-import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';
-import Translate from '~/vue_shared/translate';
-
-Vue.use(Translate);
-
-const cookieKey = 'pipeline_schedules_callout_dismissed';
-
-export default {
- name: 'PipelineSchedulesCallout',
- components: {
- GlButton,
- },
- inject: ['docsUrl', 'illustrationUrl'],
- data() {
- return {
- calloutDismissed: parseBoolean(getCookie(cookieKey)),
- };
- },
- methods: {
- dismissCallout() {
- this.calloutDismissed = true;
- setCookie(cookieKey, this.calloutDismissed);
- },
- },
-};
-</script>
-<template>
- <div v-if="!calloutDismissed" class="pipeline-schedules-user-callout user-callout">
- <div class="bordered-box landing content-block gl-p-5!" data-testid="innerContent">
- <gl-button
- category="tertiary"
- icon="close"
- :aria-label="__('Dismiss')"
- class="gl-absolute gl-top-2 gl-right-2"
- @click="dismissCallout"
- />
- <div class="svg-content">
- <img :src="illustrationUrl" />
- </div>
- <div class="user-callout-copy">
- <h4>{{ __('Scheduling Pipelines') }}</h4>
- <p>
- {{
- __(`The pipelines schedule runs pipelines in the future,
-repeatedly, for specific branches or tags.
-Those scheduled pipelines will inherit limited project access based on their associated user.`)
- }}
- </p>
- <p>
- {{ __('Learn more in the') }}
- <a :href="docsUrl" target="_blank" rel="nofollow">
- {{ __('pipeline schedules documentation') }}</a
- >.
- <!-- oneline to prevent extra space before period -->
- </p>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
index 082c345adf6..7df277641bf 100644
--- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
+++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
@@ -6,6 +6,7 @@ module Mutations
graphql_name 'ProjectCiCdSettingsUpdate'
include FindsProject
+ include Gitlab::Utils::StrongMemoize
authorize :admin_project
@@ -37,13 +38,11 @@ module Mutations
description: 'CI/CD settings after mutation.'
def resolve(full_path:, **args)
- project = authorized_find!(full_path)
-
if args[:job_token_scope_enabled]
raise Gitlab::Graphql::Errors::ArgumentError, 'job_token_scope_enabled can only be set to false'
end
- settings = project.ci_cd_settings
+ settings = project(full_path).ci_cd_settings
settings.update(args)
{
@@ -51,6 +50,14 @@ module Mutations
errors: errors_on_object(settings)
}
end
+
+ private
+
+ def project(full_path)
+ strong_memoize_with(:project, full_path) do
+ authorized_find!(full_path)
+ end
+ end
end
end
end
diff --git a/app/graphql/mutations/users/set_namespace_commit_email.rb b/app/graphql/mutations/users/set_namespace_commit_email.rb
index 72ef0635bb3..db1c33595f2 100644
--- a/app/graphql/mutations/users/set_namespace_commit_email.rb
+++ b/app/graphql/mutations/users/set_namespace_commit_email.rb
@@ -20,7 +20,7 @@ module Mutations
null: true,
description: 'User namespace commit email after mutation.'
- authorize :read_namespace
+ authorize :read_namespace_via_membership
def resolve(args)
namespace = authorized_find!(args[:namespace_id])
diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb
index 45ecbf5c084..8a49c5a6a95 100644
--- a/app/graphql/types/ci/ci_cd_setting_type.rb
+++ b/app/graphql/types/ci/ci_cd_setting_type.rb
@@ -29,6 +29,7 @@ module Types
null: true,
description: 'Whether merge pipelines are enabled.',
method: :merge_pipelines_enabled?
+ # TODO(Issue 422295): this is EE only and should be moved to the EE file
field :merge_trains_enabled,
GraphQL::Types::Boolean,
null: true,
@@ -41,3 +42,5 @@ module Types
end
end
end
+
+Types::Ci::CiCdSettingType.prepend_mod_with('Types::Ci::CiCdSettingType')
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index 3420f16213f..85bda507ff7 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -4,7 +4,7 @@ module Types
class NamespaceType < BaseObject
graphql_name 'Namespace'
- authorize :read_namespace
+ authorize :read_namespace_via_membership
field :id, GraphQL::Types::ID, null: false,
description: 'ID of the namespace.'
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index cc9003423be..8d049b8d1b1 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -19,6 +19,7 @@ class ProjectCiCdSetting < ApplicationRecord
attribute :forward_deployment_enabled, default: true
attribute :separated_caches, default: true
+ validates :merge_trains_skip_train_allowed, inclusion: { in: [true, false] }
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb
index f16d661d4bb..a7b2c40557a 100644
--- a/app/models/project_import_state.rb
+++ b/app/models/project_import_state.rb
@@ -132,10 +132,17 @@ class ProjectImportState < ApplicationRecord
alias_method :no_import?, :none?
+ # This method is coupled to the repository mirror domain.
+ # Use with caution in the importers domain. As an alternative, use the `#completed?` method.
+ # See EE-override and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4697
def in_progress?
scheduled? || started?
end
+ def completed?
+ finished? || failed? || canceled?
+ end
+
def started?
# import? does SQL work so only run it if it looks like there's an import running
status == 'started' && project.import?
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index f3a0479d3b7..30c53b978f8 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
class SentNotification < ApplicationRecord
+ include IgnorableColumns
+
+ ignore_column %i[id_convert_to_bigint], remove_with: '16.5', remove_after: '2023-09-22'
+
belongs_to :project
belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
belongs_to :recipient, class_name: "User"
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index faa83019bda..d0fb6948f2b 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -174,7 +174,9 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
prevent :read_design_activity
end
- rule { has_access }.enable :read_namespace
+ rule { has_access }.enable :read_namespace_via_membership
+
+ rule { can?(:read_namespace_via_membership) }.enable :read_namespace
rule { developer }.policy do
enable :admin_metrics_dashboard_annotation
diff --git a/app/policies/namespaces/group_project_namespace_shared_policy.rb b/app/policies/namespaces/group_project_namespace_shared_policy.rb
index 2214839fb62..b24cb5be607 100644
--- a/app/policies/namespaces/group_project_namespace_shared_policy.rb
+++ b/app/policies/namespaces/group_project_namespace_shared_policy.rb
@@ -23,6 +23,7 @@ module Namespaces
enable :read_work_item
enable :read_issue
enable :read_namespace
+ enable :read_namespace_via_membership
end
rule { can?(:create_work_item) }.enable :create_task
diff --git a/app/policies/namespaces/user_namespace_policy.rb b/app/policies/namespaces/user_namespace_policy.rb
index bfed61e72d3..f2ac0f0403d 100644
--- a/app/policies/namespaces/user_namespace_policy.rb
+++ b/app/policies/namespaces/user_namespace_policy.rb
@@ -14,6 +14,7 @@ module Namespaces
enable :import_projects
enable :admin_namespace
enable :read_namespace
+ enable :read_namespace_via_membership
enable :read_statistics
enable :create_jira_connect_subscription
enable :admin_package
diff --git a/app/services/import/github/cancel_project_import_service.rb b/app/services/import/github/cancel_project_import_service.rb
index 62cd0c95eaf..740b9e5c2e7 100644
--- a/app/services/import/github/cancel_project_import_service.rb
+++ b/app/services/import/github/cancel_project_import_service.rb
@@ -7,13 +7,13 @@ module Import
return error('Not Found', :not_found) unless authorized_to_read?
return error('Unauthorized access', :forbidden) unless authorized_to_cancel?
- if project.import_in_progress?
+ if project.import_state.completed?
+ error(cannot_cancel_error_message, :bad_request)
+ else
project.import_state.cancel
metrics.track_canceled_import
success(project: project)
- else
- error(cannot_cancel_error_message, :bad_request)
end
end
diff --git a/app/services/projects/group_links/create_service.rb b/app/services/projects/group_links/create_service.rb
index f77bae71d63..c9642fb495a 100644
--- a/app/services/projects/group_links/create_service.rb
+++ b/app/services/projects/group_links/create_service.rb
@@ -16,7 +16,7 @@ module Projects
delegate :root_ancestor, to: :project
def valid_to_create?
- can?(current_user, :read_namespace, shared_with_group) && sharing_allowed?
+ can?(current_user, :read_namespace_via_membership, shared_with_group) && sharing_allowed?
end
def build_link
diff --git a/app/services/users/set_namespace_commit_email_service.rb b/app/services/users/set_namespace_commit_email_service.rb
index 30ee597120d..775db364625 100644
--- a/app/services/users/set_namespace_commit_email_service.rb
+++ b/app/services/users/set_namespace_commit_email_service.rb
@@ -20,7 +20,7 @@ module Users
return error(_("User doesn't exist or you don't have permission to change namespace commit emails."))
end
- unless can?(target_user, :read_namespace, namespace)
+ unless can?(target_user, :read_namespace_via_membership, namespace)
return error(_("Namespace doesn't exist or you don't have permission."))
end
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 8eb0f1edee7..15a80b6c7b1 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -1,6 +1,4 @@
- breadcrumb_title _("Schedules")
- page_title _("Pipeline Schedules")
-#pipeline-schedules-callout{ data: { docs_url: help_page_path('ci/pipelines/schedules'), illustration_url: image_path('illustrations/pipeline_schedule_callout.svg') } }
-
#pipeline-schedules-app{ data: { full_path: @project.full_path, pipelines_path: project_pipelines_path(@project), new_schedule_path: new_project_pipeline_schedule_path(@project) } }
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index 210f9c35c79..d47de725603 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -4,7 +4,6 @@
%h1.page-title.gl-font-size-h-display
= s_('Pipeline|Run pipeline')
-%hr
#js-new-pipeline{ data: { project_id: @project.id,
pipelines_path: project_pipelines_path(@project),
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index c8bdab2ebf4..e190ced5073 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -38,7 +38,7 @@ module Gitlab
# client - An instance of `Gitlab::GithubImport::Client`
# hash - A Hash containing the details of the object to import.
def import(project, client, hash)
- unless project.import_state&.in_progress?
+ if project.import_state&.completed?
info(
project.id,
message: 'Project import is no longer running. Stopping worker.',
diff --git a/app/workers/concerns/gitlab/github_import/stage_methods.rb b/app/workers/concerns/gitlab/github_import/stage_methods.rb
index a5287fcfbe2..75db5589415 100644
--- a/app/workers/concerns/gitlab/github_import/stage_methods.rb
+++ b/app/workers/concerns/gitlab/github_import/stage_methods.rb
@@ -9,7 +9,7 @@ module Gitlab
return unless (project = find_project(project_id))
- unless project.import_state&.in_progress?
+ if project.import_state&.completed?
info(
project_id,
message: 'Project import is no longer running. Stopping worker.',
diff --git a/config/feature_flags/development/issue_assignees_widget.yml b/config/feature_flags/development/issue_assignees_widget.yml
index 5c9b7df941f..5163a345a3b 100644
--- a/config/feature_flags/development/issue_assignees_widget.yml
+++ b/config/feature_flags/development/issue_assignees_widget.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328185
milestone: '13.11'
type: development
group: group::project management
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/merge_trains_skip_train.yml b/config/feature_flags/development/merge_trains_skip_train.yml
new file mode 100644
index 00000000000..3d60acef457
--- /dev/null
+++ b/config/feature_flags/development/merge_trains_skip_train.yml
@@ -0,0 +1,8 @@
+---
+name: merge_trains_skip_train
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129422
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/422111
+milestone: '16.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml b/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml
new file mode 100644
index 00000000000..5d3f4e92355
--- /dev/null
+++ b/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml
@@ -0,0 +1,6 @@
+---
+migration_job_name: BackfillFindingIdInVulnerabilities
+description: Backfills finding_id column on vulnerabilities table for a proper 1:1 relation
+feature_category: vulnerability_management
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/418971
+milestone: 16.4
diff --git a/db/migrate/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion.rb b/db/migrate/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion.rb
new file mode 100644
index 00000000000..8dbeb873bab
--- /dev/null
+++ b/db/migrate/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+class RestartSelfHostedSentNotificationsBigintConversion < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::MigrationHelpers::ConvertToBigint
+
+ disable_ddl_transaction!
+
+ TABLE = :sent_notifications
+ COLUMNS = %i[id]
+
+ def up
+ return if should_skip? || id_is_bigint? || id_convert_to_bigint_exist?
+
+ initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def down
+ return if should_skip? || id_is_bigint?
+
+ revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def should_skip?
+ com_or_dev_or_test_but_not_jh?
+ end
+
+ def id_is_bigint?
+ table_columns = columns(TABLE)
+ column_id = table_columns.find { |c| c.name == 'id' }
+ column_id.sql_type == 'bigint'
+ end
+
+ def id_convert_to_bigint_exist?
+ column_exists?(TABLE.to_s, 'id_convert_to_bigint')
+ end
+end
diff --git a/db/migrate/20230906204935_restart_self_hosted_sent_notifications_backfill.rb b/db/migrate/20230906204935_restart_self_hosted_sent_notifications_backfill.rb
new file mode 100644
index 00000000000..21c1798179f
--- /dev/null
+++ b/db/migrate/20230906204935_restart_self_hosted_sent_notifications_backfill.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+class RestartSelfHostedSentNotificationsBackfill < Gitlab::Database::Migration[2.1]
+ include Gitlab::Database::MigrationHelpers::ConvertToBigint
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ TABLE = :sent_notifications
+ COLUMNS = %i[id]
+
+ def up
+ return if should_skip? || id_is_bigint? || already_backfilled?
+
+ # rubocop: disable Migration/BatchMigrationsPostOnly
+ delete_batched_background_migration(
+ 'CopyColumnUsingBackgroundMigrationJob',
+ :sent_notifications,
+ :id,
+ [["id"], ["id_convert_to_bigint"]]
+ )
+ # rubocop: enable Migration/BatchMigrationsPostOnly
+
+ backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def down
+ return if should_skip? || id_is_bigint? || already_backfilled?
+
+ revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def should_skip?
+ com_or_dev_or_test_but_not_jh?
+ end
+
+ def id_is_bigint?
+ table_columns = columns(TABLE)
+ column_id = table_columns.find { |c| c.name == 'id' }
+ column_id.sql_type == 'bigint'
+ end
+
+ def already_backfilled?
+ res = connection.execute <<~SQL
+ SELECT
+ id_convert_to_bigint
+ FROM
+ sent_notifications
+ ORDER BY
+ id ASC
+ LIMIT 1
+ SQL
+ return false if res.ntuples == 0
+
+ res.first['id_convert_to_bigint'].to_i != 0
+ end
+end
diff --git a/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb b/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb
new file mode 100644
index 00000000000..eee1ba6f781
--- /dev/null
+++ b/db/migrate/20230918194153_add_merge_immediately_to_ci_cd_settings.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddMergeImmediatelyToCiCdSettings < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ add_column :project_ci_cd_settings, :merge_trains_skip_train_allowed, :boolean, default: false, null: false
+ end
+
+ def down
+ remove_column :project_ci_cd_settings, :merge_trains_skip_train_allowed
+ end
+end
diff --git a/db/post_migrate/20230912105945_queue_backfill_finding_id_in_vulnerabilities.rb b/db/post_migrate/20230912105945_queue_backfill_finding_id_in_vulnerabilities.rb
new file mode 100644
index 00000000000..3275f6e729d
--- /dev/null
+++ b/db/post_migrate/20230912105945_queue_backfill_finding_id_in_vulnerabilities.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class QueueBackfillFindingIdInVulnerabilities < Gitlab::Database::Migration[2.1]
+ MIGRATION = "BackfillFindingIdInVulnerabilities"
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1000
+ SUB_BATCH_SIZE = 100
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :vulnerabilities,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :vulnerabilities, :id, [])
+ end
+end
diff --git a/db/schema_migrations/20230906204934 b/db/schema_migrations/20230906204934
new file mode 100644
index 00000000000..d15bd01b46f
--- /dev/null
+++ b/db/schema_migrations/20230906204934
@@ -0,0 +1 @@
+63b9153f085cb11279e84cb0e67a12987eaa6e20825e81d30d88054105b29825 \ No newline at end of file
diff --git a/db/schema_migrations/20230906204935 b/db/schema_migrations/20230906204935
new file mode 100644
index 00000000000..fa8c281171b
--- /dev/null
+++ b/db/schema_migrations/20230906204935
@@ -0,0 +1 @@
+9d1531d614e9a156f0d2aa9334aeab436ada293f37ef48223de76a360e85ed53 \ No newline at end of file
diff --git a/db/schema_migrations/20230912105945 b/db/schema_migrations/20230912105945
new file mode 100644
index 00000000000..2ce3fedd036
--- /dev/null
+++ b/db/schema_migrations/20230912105945
@@ -0,0 +1 @@
+d8e5e8780310b9877cb3f9696b5596447f3fca1c01770495cf9942041203b430 \ No newline at end of file
diff --git a/db/schema_migrations/20230918194153 b/db/schema_migrations/20230918194153
new file mode 100644
index 00000000000..2fd045aa9ed
--- /dev/null
+++ b/db/schema_migrations/20230918194153
@@ -0,0 +1 @@
+1ec3edbe609cd0142790b28c3b55e50ae36be4119f741ce970b36fd8a788a2ce \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index e86fe42ecb4..9283a0c4800 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -21348,7 +21348,8 @@ CREATE TABLE project_ci_cd_settings (
separated_caches boolean DEFAULT true NOT NULL,
allow_fork_pipelines_to_run_in_parent_project boolean DEFAULT true NOT NULL,
inbound_job_token_scope_enabled boolean DEFAULT true NOT NULL,
- forward_deployment_rollback_allowed boolean DEFAULT true NOT NULL
+ forward_deployment_rollback_allowed boolean DEFAULT true NOT NULL,
+ merge_trains_skip_train_allowed boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE project_ci_cd_settings_id_seq
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 4c44bfa0560..65736f10ba9 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5594,6 +5594,7 @@ Input type: `ProjectCiCdSettingsUpdateInput`
| <a id="mutationprojectcicdsettingsupdatekeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Indicates if the latest artifact should be kept for the project. |
| <a id="mutationprojectcicdsettingsupdatemergepipelinesenabled"></a>`mergePipelinesEnabled` | [`Boolean`](#boolean) | Indicates if merge pipelines are enabled for the project. |
| <a id="mutationprojectcicdsettingsupdatemergetrainsenabled"></a>`mergeTrainsEnabled` | [`Boolean`](#boolean) | Indicates if merge trains are enabled for the project. |
+| <a id="mutationprojectcicdsettingsupdatemergetrainsskiptrainallowed"></a>`mergeTrainsSkipTrainAllowed` | [`Boolean`](#boolean) | Indicates whether an option is allowed to merge without refreshing the merge train. Ignored unless the `merge_trains_skip_train` feature flag is also enabled. |
#### Fields
@@ -23357,6 +23358,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectcicdsettingkeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Whether to keep the latest builds artifacts. |
| <a id="projectcicdsettingmergepipelinesenabled"></a>`mergePipelinesEnabled` | [`Boolean`](#boolean) | Whether merge pipelines are enabled. |
| <a id="projectcicdsettingmergetrainsenabled"></a>`mergeTrainsEnabled` | [`Boolean`](#boolean) | Whether merge trains are enabled. |
+| <a id="projectcicdsettingmergetrainsskiptrainallowed"></a>`mergeTrainsSkipTrainAllowed` | [`Boolean!`](#boolean) | Whether merge immediately is allowed for merge trains. |
| <a id="projectcicdsettingproject"></a>`project` | [`Project`](#project) | Project the CI/CD settings belong to. |
### `ProjectConversations`
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 8ebd7f83acb..c36ee9dc064 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -7,7 +7,7 @@ module API
LOG_FILENAME = Rails.root.join("log", "api_json.log")
- NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
+ NO_SLASH_URL_PART_REGEX = %r{[^/]+}
NAMESPACE_OR_PROJECT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze
COMMIT_ENDPOINT_REQUIREMENTS = NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze
USER_REQUIREMENTS = { user_id: NO_SLASH_URL_PART_REGEX }.freeze
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 7c64dc2f877..9ceccbb5635 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -3,7 +3,7 @@
module API
class DebianGroupPackages < ::API::Base
PACKAGE_FILE_REQUIREMENTS = ::API::DebianProjectPackages::PACKAGE_FILE_REQUIREMENTS.merge(
- project_id: %r{[0-9]+}.freeze
+ project_id: %r{[0-9]+}
).freeze
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
diff --git a/lib/api/go_proxy.rb b/lib/api/go_proxy.rb
index 8fde40a4713..3933e07d150 100755
--- a/lib/api/go_proxy.rb
+++ b/lib/api/go_proxy.rb
@@ -10,7 +10,7 @@ module API
urgency :low
# basic semver, except case encoded (A => !a)
- MODULE_VERSION_REGEX = /v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.!a-z0-9]+))?(?:\+([-.!a-z0-9]+))?/.freeze
+ MODULE_VERSION_REGEX = /v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([-.!a-z0-9]+))?(?:\+([-.!a-z0-9]+))?/
MODULE_VERSION_REQUIREMENTS = { module_version: MODULE_VERSION_REGEX }.freeze
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index e967b88e500..9e04cf955df 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -19,7 +19,7 @@ module API
API_TOKEN_ENV = 'gitlab.api.token'
API_EXCEPTION_ENV = 'gitlab.api.exception'
API_RESPONSE_STATUS_CODE = 'gitlab.api.response_status_code'
- INTEGER_ID_REGEX = /^-?\d+$/.freeze
+ INTEGER_ID_REGEX = /^-?\d+$/
def logger
API.logger
@@ -237,7 +237,7 @@ module API
end
def check_namespace_access(namespace)
- return namespace if can?(current_user, :read_namespace, namespace)
+ return namespace if can?(current_user, :read_namespace_via_membership, namespace)
not_found!('Namespace')
end
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index 7348ed612fc..0ce5cdd06de 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -9,7 +9,7 @@
module API
module V3
class Github < ::API::Base
- NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
+ NO_SLASH_URL_PART_REGEX = %r{[^/]+}
ENDPOINT_REQUIREMENTS = {
namespace: NO_SLASH_URL_PART_REGEX,
project: NO_SLASH_URL_PART_REGEX,
diff --git a/lib/api/validations/validators/git_ref.rb b/lib/api/validations/validators/git_ref.rb
index 711c272ab4e..4e113a4ef67 100644
--- a/lib/api/validations/validators/git_ref.rb
+++ b/lib/api/validations/validators/git_ref.rb
@@ -10,7 +10,7 @@ module API
# We have skipped some checks that are optional and can be skipped for exception.
# We also check for control characters, More info on ctrl chars - https://ruby-doc.org/core-2.7.0/Regexp.html#class-Regexp-label-Character+Classes
INVALID_CHARS = Regexp.union('..', '\\', '@', '@{', ' ', '~', '^', ':', '*', '?', '[', /[[:cntrl:]]/).freeze
- GIT_REF_LENGTH = (1..1024).freeze
+ GIT_REF_LENGTH = (1..1024)
def validate_param!(attr_name, params)
revision = params[attr_name]
diff --git a/lib/atlassian/jira_connect/jwt/asymmetric.rb b/lib/atlassian/jira_connect/jwt/asymmetric.rb
index 8698be70eb9..470b1fc8c9b 100644
--- a/lib/atlassian/jira_connect/jwt/asymmetric.rb
+++ b/lib/atlassian/jira_connect/jwt/asymmetric.rb
@@ -14,7 +14,7 @@ module Atlassian
ALGORITHM = 'RS256'
DEFAULT_PUBLIC_KEY_CDN_URL = 'https://connect-install-keys.atlassian.com'
PROXY_PUBLIC_KEY_PATH = '/-/jira_connect/public_keys'
- UUID4_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/.freeze
+ UUID4_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/
def initialize(token, verification_claims)
@token = token
diff --git a/lib/banzai/color_parser.rb b/lib/banzai/color_parser.rb
index cce79e73d2d..6d01d51955c 100644
--- a/lib/banzai/color_parser.rb
+++ b/lib/banzai/color_parser.rb
@@ -2,13 +2,13 @@
module Banzai
module ColorParser
- ALPHA = /0(?:\.\d+)?|\.\d+|1(?:\.0+)?/.freeze # 0.0..1.0
- PERCENTS = /(?:\d{1,2}|100)%/.freeze # 00%..100%
- ALPHA_CHANNEL = /(?:,\s*(?:#{ALPHA}|#{PERCENTS}))?/.freeze
- BITS = /\d{1,2}|1\d\d|2(?:[0-4]\d|5[0-5])/.freeze # 00..255
- DEGS = /-?\d+(?:deg)?/i.freeze # [-]digits[deg]
- RADS = /-?(?:\d+(?:\.\d+)?|\.\d+)rad/i.freeze # [-](digits[.digits] OR .digits)rad
- HEX_FORMAT = /\#(?:\h{3}|\h{4}|\h{6}|\h{8})/.freeze
+ ALPHA = /0(?:\.\d+)?|\.\d+|1(?:\.0+)?/ # 0.0..1.0
+ PERCENTS = /(?:\d{1,2}|100)%/ # 00%..100%
+ ALPHA_CHANNEL = /(?:,\s*(?:#{ALPHA}|#{PERCENTS}))?/
+ BITS = /\d{1,2}|1\d\d|2(?:[0-4]\d|5[0-5])/ # 00..255
+ DEGS = /-?\d+(?:deg)?/i # [-]digits[deg]
+ RADS = /-?(?:\d+(?:\.\d+)?|\.\d+)rad/i # [-](digits[.digits] OR .digits)rad
+ HEX_FORMAT = /\#(?:\h{3}|\h{4}|\h{6}|\h{8})/
RGB_FORMAT = %r{
(?:rgba?
\(
@@ -20,7 +20,7 @@ module Banzai
#{ALPHA_CHANNEL}
\)
)
- }xi.freeze
+ }xi
HSL_FORMAT = %r{
(?:hsla?
\(
@@ -28,11 +28,11 @@ module Banzai
#{ALPHA_CHANNEL}
\)
)
- }xi.freeze
+ }xi
FORMATS = [HEX_FORMAT, RGB_FORMAT, HSL_FORMAT].freeze
- COLOR_FORMAT = /\A(#{Regexp.union(FORMATS)})\z/ix.freeze
+ COLOR_FORMAT = /\A(#{Regexp.union(FORMATS)})\z/ix
# Public: Analyzes whether the String is a color code.
#
diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
index 4158aa8a5ec..3d425b9795f 100644
--- a/lib/banzai/filter/ascii_doc_sanitization_filter.rb
+++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb
@@ -7,7 +7,7 @@ module Banzai
# Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
# Anchor link prefixed by "user-content-" pattern
- PREFIXED_ID_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
+ PREFIXED_ID_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/
SECTION_HEADINGS = %w[h2 h3 h4 h5 h6].freeze
# Footnote link patterns
diff --git a/lib/banzai/filter/attributes_filter.rb b/lib/banzai/filter/attributes_filter.rb
index ab50b3d6858..98b0ed8cc22 100644
--- a/lib/banzai/filter/attributes_filter.rb
+++ b/lib/banzai/filter/attributes_filter.rb
@@ -16,9 +16,9 @@ module Banzai
CSS = 'img'
XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze
- ATTRIBUTES_PATTERN = %r{\A(?<matched>\{(?<attributes>.{1,100})\})}.freeze
- WIDTH_HEIGHT_REGEX = %r{\A(?<name>height|width)="?(?<size>[\w%]{1,10})"?\z}.freeze
- VALID_SIZE_REGEX = %r{\A\d{1,4}(%|px)?\z}.freeze
+ ATTRIBUTES_PATTERN = %r{\A(?<matched>\{(?<attributes>.{1,100})\})}
+ WIDTH_HEIGHT_REGEX = %r{\A(?<name>height|width)="?(?<size>[\w%]{1,10})"?\z}
+ VALID_SIZE_REGEX = %r{\A\d{1,4}(%|px)?\z}
def call
doc.xpath(XPATH).each do |img|
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index e877e5f316c..87e2d94af94 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -36,7 +36,7 @@ module Banzai
# Rubular: http://rubular.com/r/nrL3r9yUiq
# Note that it's not possible to use Gitlab::UntrustedRegexp for LINK_PATTERN,
# as `(?<!` is unsupported in `re2`, see https://github.com/google/re2/wiki/Syntax
- LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://[^\s>]+)(?<!\?|!|\.|,|:)}.freeze
+ LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://[^\s>]+)(?<!\?|!|\.|,|:)}
ENTITY_UNTRUSTED = '((?:&[\w#]+;)+)\z'
ENTITY_UNTRUSTED_REGEX = Gitlab::UntrustedRegexp.new(ENTITY_UNTRUSTED, multiline: false)
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index d4ff7d4c6b5..34b9fd63b1c 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -32,7 +32,7 @@ module Banzai
)
\n\ *>>>\ *(?=\n$|\z)
)
- }mx.freeze
+ }mx
def initialize(text, context = nil, result = nil)
super text, context, result
diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb
index f10efdccdf1..ada74d613f9 100644
--- a/lib/banzai/filter/footnote_filter.rb
+++ b/lib/banzai/filter/footnote_filter.rb
@@ -19,8 +19,8 @@ module Banzai
class FootnoteFilter < HTML::Pipeline::Filter
FOOTNOTE_ID_PREFIX = 'fn-'
FOOTNOTE_LINK_ID_PREFIX = 'fnref-'
- FOOTNOTE_LI_REFERENCE_PATTERN = /\A#{FOOTNOTE_ID_PREFIX}.+\z/.freeze
- FOOTNOTE_LINK_REFERENCE_PATTERN = /\A#{FOOTNOTE_LINK_ID_PREFIX}.+\z/.freeze
+ FOOTNOTE_LI_REFERENCE_PATTERN = /\A#{FOOTNOTE_ID_PREFIX}.+\z/
+ FOOTNOTE_LINK_REFERENCE_PATTERN = /\A#{FOOTNOTE_LINK_ID_PREFIX}.+\z/
CSS_SECTION = "section[data-footnotes]"
XPATH_SECTION = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_SECTION).freeze
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index ade4f82e54b..9c3ad4c6a0c 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -51,10 +51,10 @@ module Banzai
# See https://github.com/gollum/gollum/wiki
#
# Rubular: http://rubular.com/r/7dQnE5CUCH
- TAGS_PATTERN = /\[\[(.+?)\]\]/.freeze
+ TAGS_PATTERN = /\[\[(.+?)\]\]/
# Pattern to match allowed image extensions
- ALLOWED_IMAGE_EXTENSIONS = /.+(jpg|png|gif|svg|bmp)\z/i.freeze
+ ALLOWED_IMAGE_EXTENSIONS = /.+(jpg|png|gif|svg|bmp)\z/i
# Do not perform linking inside these tags.
IGNORED_ANCESTOR_TAGS = %w[pre code tt].to_set
diff --git a/lib/banzai/filter/markdown_post_escape_filter.rb b/lib/banzai/filter/markdown_post_escape_filter.rb
index 4d37fba33aa..90b9555df1d 100644
--- a/lib/banzai/filter/markdown_post_escape_filter.rb
+++ b/lib/banzai/filter/markdown_post_escape_filter.rb
@@ -5,9 +5,9 @@ module Banzai
# See comments in MarkdownPreEscapeFilter for details on strategy
class MarkdownPostEscapeFilter < HTML::Pipeline::Filter
LITERAL_KEYWORD = MarkdownPreEscapeFilter::LITERAL_KEYWORD
- LITERAL_REGEX = %r{#{LITERAL_KEYWORD}-(.*?)-#{LITERAL_KEYWORD}}.freeze
- NOT_LITERAL_REGEX = %r{#{LITERAL_KEYWORD}-((%5C|\\).+?)-#{LITERAL_KEYWORD}}.freeze
- SPAN_REGEX = %r{<span data-escaped-char>(.*?)</span>}.freeze
+ LITERAL_REGEX = %r{#{LITERAL_KEYWORD}-(.*?)-#{LITERAL_KEYWORD}}
+ NOT_LITERAL_REGEX = %r{#{LITERAL_KEYWORD}-((%5C|\\).+?)-#{LITERAL_KEYWORD}}
+ SPAN_REGEX = %r{<span data-escaped-char>(.*?)</span>}
XPATH_A = Gitlab::Utils::Nokogiri.css_to_xpath('a').freeze
XPATH_LANG_TAG = Gitlab::Utils::Nokogiri.css_to_xpath('pre').freeze
diff --git a/lib/banzai/filter/markdown_pre_escape_filter.rb b/lib/banzai/filter/markdown_pre_escape_filter.rb
index 8cc7b0defd6..b6f063ece57 100644
--- a/lib/banzai/filter/markdown_pre_escape_filter.rb
+++ b/lib/banzai/filter/markdown_pre_escape_filter.rb
@@ -57,7 +57,7 @@ module Banzai
].freeze
TARGET_CHARS = ESCAPABLE_CHARS.pluck(:char).join.freeze
- ASCII_PUNCTUATION = %r{(\\[#{TARGET_CHARS}])}.freeze
+ ASCII_PUNCTUATION = %r{(\\[#{TARGET_CHARS}])}
LITERAL_KEYWORD = 'cmliteral'
def call
diff --git a/lib/banzai/filter/references/abstract_reference_filter.rb b/lib/banzai/filter/references/abstract_reference_filter.rb
index 3e48fe33b03..c3c5103106b 100644
--- a/lib/banzai/filter/references/abstract_reference_filter.rb
+++ b/lib/banzai/filter/references/abstract_reference_filter.rb
@@ -20,7 +20,7 @@ module Banzai
# transitory value (it never gets saved) we can initialize once, and it
# doesn't matter if it changes on a restart.
REFERENCE_PLACEHOLDER = "_reference_#{SecureRandom.hex(16)}_"
- REFERENCE_PLACEHOLDER_PATTERN = %r{#{REFERENCE_PLACEHOLDER}(\d+)}.freeze
+ REFERENCE_PLACEHOLDER_PATTERN = %r{#{REFERENCE_PLACEHOLDER}(\d+)}
# Public: Find references in text (like `!123` for merge requests)
#
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index 15013c8595e..f33bc9cd621 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -7,7 +7,7 @@ module Banzai
# Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
class SanitizationFilter < Banzai::Filter::BaseSanitizationFilter
# Styles used by Markdown for table alignment
- TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/.freeze
+ TABLE_ALIGNMENT_PATTERN = /text-align: (?<alignment>center|left|right)/
def customize_allowlist(allowlist)
allowlist[:allow_comments] = context[:allow_comments]
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index e8a7677b102..4f39a25ff68 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -32,7 +32,7 @@ module Banzai
XPATH = 'descendant-or-self::li[input[@data-inapplicable]] | descendant-or-self::li[p[input[@data-inapplicable]]]'
INAPPLICABLE = '[~]'
- INAPPLICABLEPATTERN = /\[~\]/.freeze
+ INAPPLICABLEPATTERN = /\[~\]/
# Pattern used to identify all task list items.
# Useful when you need iterate over all items.
@@ -46,7 +46,7 @@ module Banzai
#{INAPPLICABLEPATTERN}
)
(?=\s) # followed by whitespace
- /x.freeze
+ /x
# Force the gem's constant to use our new one
superclass.send(:remove_const, :ItemPattern) # rubocop: disable GitlabSecurity/PublicSend
diff --git a/lib/bulk_imports/common/pipelines/uploads_pipeline.rb b/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
index ea17af36c9a..10967796e2f 100644
--- a/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
+++ b/lib/bulk_imports/common/pipelines/uploads_pipeline.rb
@@ -6,7 +6,7 @@ module BulkImports
class UploadsPipeline
include Pipeline
- AVATAR_PATTERN = %r{.*\/#{BulkImports::UploadsExportService::AVATAR_PATH}\/(?<identifier>.*)}.freeze
+ AVATAR_PATTERN = %r{.*\/#{BulkImports::UploadsExportService::AVATAR_PATH}\/(?<identifier>.*)}
AvatarLoadingError = Class.new(StandardError)
diff --git a/lib/bulk_imports/file_downloads/filename_fetch.rb b/lib/bulk_imports/file_downloads/filename_fetch.rb
index b6bb0fd8c81..ac58e0f8fd6 100644
--- a/lib/bulk_imports/file_downloads/filename_fetch.rb
+++ b/lib/bulk_imports/file_downloads/filename_fetch.rb
@@ -3,7 +3,7 @@
module BulkImports
module FileDownloads
module FilenameFetch
- REMOTE_FILENAME_PATTERN = %r{filename="(?<filename>[^"]+)"}.freeze
+ REMOTE_FILENAME_PATTERN = %r{filename="(?<filename>[^"]+)"}
FILENAME_SIZE_LIMIT = 255 # chars before the extension
def raise_error(message)
diff --git a/lib/error_tracking/sentry_client/pagination_parser.rb b/lib/error_tracking/sentry_client/pagination_parser.rb
index c6a42a6def2..090707c21ab 100644
--- a/lib/error_tracking/sentry_client/pagination_parser.rb
+++ b/lib/error_tracking/sentry_client/pagination_parser.rb
@@ -3,7 +3,7 @@
module ErrorTracking
class SentryClient
module PaginationParser
- PATTERN = /rel="(?<direction>\w+)";\sresults="(?<results>\w+)";\scursor="(?<cursor>.+)"/.freeze
+ PATTERN = /rel="(?<direction>\w+)";\sresults="(?<results>\w+)";\scursor="(?<cursor>.+)"/
def self.parse(headers)
links = headers['link'].to_s.split(',')
diff --git a/lib/expand_variables.rb b/lib/expand_variables.rb
index 51a66958ba0..f565eb105ae 100644
--- a/lib/expand_variables.rb
+++ b/lib/expand_variables.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module ExpandVariables
- VARIABLES_REGEXP = /\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/.freeze
+ VARIABLES_REGEXP = /\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/
class << self
def expand(value, variables, expand_file_refs: true)
diff --git a/lib/feature/definition.rb b/lib/feature/definition.rb
index 2bad7cfd33d..af60fb95c53 100644
--- a/lib/feature/definition.rb
+++ b/lib/feature/definition.rb
@@ -7,7 +7,7 @@ module Feature
attr_reader :path
attr_reader :attributes
- VALID_FEATURE_NAME = %r{^#{Gitlab::Regex.sep_by_1('_', /[a-z0-9]+/)}$}.freeze
+ VALID_FEATURE_NAME = %r{^#{Gitlab::Regex.sep_by_1('_', /[a-z0-9]+/)}$}
PARAMS.each do |param|
define_method(param) do
diff --git a/lib/gitaly/server.rb b/lib/gitaly/server.rb
index a816dd89e9c..38bb1f649c9 100644
--- a/lib/gitaly/server.rb
+++ b/lib/gitaly/server.rb
@@ -2,7 +2,7 @@
module Gitaly
class Server
- SHA_VERSION_REGEX = /\A\d+\.\d+\.\d+-\d+-g([a-f0-9]{8})\z/.freeze
+ SHA_VERSION_REGEX = /\A\d+\.\d+\.\d+-\d+-g([a-f0-9]{8})\z/
DEFAULT_REPLICATION_FACTOR = 1
class << self
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index d4cd62a9c21..0875b14f7d0 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -44,7 +44,7 @@ module Gitlab
end
end
- APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}.freeze
+ APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
VERSION = File.read(root.join("VERSION")).strip.freeze
INSTALLATION_TYPE = File.read(root.join("INSTALLATION_TYPE")).strip.freeze
HTTP_PROXY_ENV_VARS = %w[http_proxy https_proxy HTTP_PROXY HTTPS_PROXY].freeze
diff --git a/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities.rb b/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities.rb
new file mode 100644
index 00000000000..e3b77e3c7cd
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Backfills vulnerabilities.finding_id column based on vulnerability_occurrences.vulnerability_id column
+ class BackfillFindingIdInVulnerabilities < BatchedMigrationJob
+ operation_name :backfill_finding_id_in_vulnerabilities_table
+ scope_to ->(relation) { relation.where(finding_id: nil) }
+ feature_category :vulnerability_management
+
+ class VulnerabilitiesFindings < ApplicationRecord # rubocop:disable Style/Documentation
+ self.table_name = "vulnerability_occurrences"
+ end
+
+ def perform
+ each_sub_batch do |sub_batch|
+ connection.execute <<~SQL
+ UPDATE vulnerabilities
+ SET finding_id = vulnerability_occurrences.id
+ FROM vulnerability_occurrences
+ WHERE vulnerabilities.id IN (#{sub_batch.select(:id).to_sql})
+ AND vulnerabilities.id = vulnerability_occurrences.vulnerability_id
+ SQL
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb b/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb
index 878f89a8b3d..c8e6841c2ae 100644
--- a/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb
+++ b/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification.rb
@@ -10,14 +10,14 @@ module Gitlab
# - https://docs.drone.io/pipeline/environment/reference/drone-system-hostname/
'Integrations::DroneCi' => [
:drone_url,
- /\Acloud\.drone\.io\z/i.freeze
+ /\Acloud\.drone\.io\z/i
],
# This matches the logic in `Integrations::Teamcity#url_is_saas?`
# - https://gitlab.com/gitlab-org/gitlab/blob/65b7fc1ad1ad33247890324e9a3396993b7718a1/app/models/integrations/teamcity.rb#L117-122
# - https://www.jetbrains.com/help/teamcity/cloud/migrate-from-teamcity-on-premises-to-teamcity-cloud.html#Migration+Process
'Integrations::Teamcity' => [
:teamcity_url,
- /\A[^\.]+\.teamcity\.com\z/i.freeze
+ /\A[^\.]+\.teamcity\.com\z/i
]
# Other CI integrations which don't seem to have a SaaS offering:
diff --git a/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb b/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
index 6d59a5c8651..de3c52719c3 100644
--- a/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
+++ b/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb
@@ -20,7 +20,7 @@ module Gitlab
# 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
+ JIRA_CLOUD_REGEX = %r{^https?://[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.atlassian\.net$}ix
def perform
cloud = []
diff --git a/lib/gitlab/changelog/generator.rb b/lib/gitlab/changelog/generator.rb
index a80ca0728f9..0e546c5eb60 100644
--- a/lib/gitlab/changelog/generator.rb
+++ b/lib/gitlab/changelog/generator.rb
@@ -6,7 +6,7 @@ module Gitlab
class Generator
# The regex used to parse a release header.
RELEASE_REGEX =
- /^##\s+(?<version>#{Gitlab::Regex.unbounded_semver_regex})/.freeze
+ /^##\s+(?<version>#{Gitlab::Regex.unbounded_semver_regex})/
# The `input` argument must be a `String` containing the existing
# changelog Markdown. If no changelog exists, this should be an empty
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index 5748b8e34cf..7d9235ac460 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -11,8 +11,8 @@ module Gitlab
ParserError = Class.new(StandardError)
InvalidStreamError = Class.new(StandardError)
- VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/.freeze
- INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}.freeze
+ VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
+ INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
attr_reader :stream, :path, :full_version
diff --git a/lib/gitlab/ci/config/entry/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb
index 27206d7e3a8..3fd07811daf 100644
--- a/lib/gitlab/ci/config/entry/artifacts.rb
+++ b/lib/gitlab/ci/config/entry/artifacts.rb
@@ -14,7 +14,7 @@ module Gitlab
ALLOWED_WHEN = %w[on_success on_failure always].freeze
ALLOWED_KEYS = %i[name untracked paths reports when expire_in expose_as exclude public].freeze
- EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/.freeze
+ EXPOSE_AS_REGEX = /\A\w[-\w ]*\z/
EXPOSE_AS_ERROR_MESSAGE = "can contain only letters, digits, '-', '_' and spaces"
attributes ALLOWED_KEYS
diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb
index efba81c7420..e3a87c8576f 100644
--- a/lib/gitlab/ci/config/external/file/base.rb
+++ b/lib/gitlab/ci/config/external/file/base.rb
@@ -10,7 +10,7 @@ module Gitlab
attr_reader :location, :params, :context, :errors
- YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i.freeze
+ YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i
def initialize(params, context)
@params = params
diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb
index d95ecff85cd..32592a10cb0 100644
--- a/lib/gitlab/ci/parsers/test/junit.rb
+++ b/lib/gitlab/ci/parsers/test/junit.rb
@@ -6,7 +6,7 @@ module Gitlab
module Test
class Junit
JunitParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
- ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/.freeze
+ ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/
def parse!(xml_data, test_report, job:)
test_suite = test_report.get_suite(job.test_suite_name)
diff --git a/lib/gitlab/ci/pipeline/chain/skip.rb b/lib/gitlab/ci/pipeline/chain/skip.rb
index 76dfb4cbd87..152ea700eb7 100644
--- a/lib/gitlab/ci/pipeline/chain/skip.rb
+++ b/lib/gitlab/ci/pipeline/chain/skip.rb
@@ -7,7 +7,7 @@ module Gitlab
class Skip < Chain::Base
include ::Gitlab::Utils::StrongMemoize
- SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i.freeze
+ SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i
def perform!
if skipped?
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/and.rb b/lib/gitlab/ci/pipeline/expression/lexeme/and.rb
index 422735bd104..70d439e2d20 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/and.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/and.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class And < Lexeme::LogicalOperator
- PATTERN = /&&/.freeze
+ PATTERN = /&&/
def evaluate(variables = {})
@left.evaluate(variables) && @right.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb b/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
index d35be12c996..9a45105eeaf 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/equals.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class Equals < Lexeme::LogicalOperator
- PATTERN = /==/.freeze
+ PATTERN = /==/
def evaluate(variables = {})
@left.evaluate(variables) == @right.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
index c4f06c4686d..35e08776820 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class Matches < Lexeme::LogicalOperator
- PATTERN = /=~/.freeze
+ PATTERN = /=~/
def evaluate(variables = {})
text = @left.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb
index 64485a7e6b3..54ae3b0c369 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class NotEquals < Lexeme::LogicalOperator
- PATTERN = /!=/.freeze
+ PATTERN = /!=/
def evaluate(variables = {})
@left.evaluate(variables) != @right.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb
index 99d9206da74..4cd9e3f3572 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class NotMatches < Lexeme::LogicalOperator
- PATTERN = /\!~/.freeze
+ PATTERN = /\!~/
def evaluate(variables = {})
text = @left.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/null.rb b/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
index e7f7945532b..89b7e0b102e 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/null.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class Null < Lexeme::Value
- PATTERN = /null/.freeze
+ PATTERN = /null/
def initialize(value = nil)
super
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/or.rb b/lib/gitlab/ci/pipeline/expression/lexeme/or.rb
index c7d653ac859..1a7b619c49c 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/or.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/or.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class Or < Lexeme::LogicalOperator
- PATTERN = /\|\|/.freeze
+ PATTERN = /\|\|/
def evaluate(variables = {})
@left.evaluate(variables) || @right.evaluate(variables)
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb b/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb
index b0ca26c9f5d..29b5e47a65f 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_close.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class ParenthesisClose < Lexeme::Operator
- PATTERN = /\)/.freeze
+ PATTERN = /\)/
def self.type
:parenthesis_close
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb b/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb
index 924fe0663ab..80f92609154 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/parenthesis_open.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class ParenthesisOpen < Lexeme::Operator
- PATTERN = /\(/.freeze
+ PATTERN = /\(/
def self.type
:parenthesis_open
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
index cd4106b16bb..43e9e4bba83 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb
@@ -8,7 +8,7 @@ module Gitlab
require_dependency 're2'
class Pattern < Lexeme::Value
- PATTERN = %r{^\/([^\/]|\\/)+[^\\]\/[ismU]*}.freeze
+ PATTERN = %r{^\/([^\/]|\\/)+[^\\]\/[ismU]*}
def initialize(regexp)
super(regexp.gsub(%r{\\/}, '/'))
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/string.rb b/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
index 798cea34db6..c43150125b7 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/string.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class String < Lexeme::Value
- PATTERN = /("(?<string>.*?)")|('(?<string>.*?)')/.freeze
+ PATTERN = /("(?<string>.*?)")|('(?<string>.*?)')/
def evaluate(variables = {})
@value.to_s
diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb b/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
index 6da88fd287e..2ecd50d32e4 100644
--- a/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
+++ b/lib/gitlab/ci/pipeline/expression/lexeme/variable.rb
@@ -6,7 +6,7 @@ module Gitlab
module Expression
module Lexeme
class Variable < Lexeme::Value
- PATTERN = /\$(?<name>\w+)/.freeze
+ PATTERN = /\$(?<name>\w+)/
def evaluate(variables = {})
unless variables.is_a?(ActiveSupport::HashWithIndifferentAccess)
diff --git a/lib/gitlab/ci/trace/section_parser.rb b/lib/gitlab/ci/trace/section_parser.rb
index f33f8cc56c1..a6c1bf28f24 100644
--- a/lib/gitlab/ci/trace/section_parser.rb
+++ b/lib/gitlab/ci/trace/section_parser.rb
@@ -74,7 +74,7 @@ module Gitlab
end
def beginning_of_section_regex
- @beginning_of_section_regex ||= /section_/.freeze
+ @beginning_of_section_regex ||= /section_/
end
def find_next_marker(scanner)
diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb
index 73452d83bce..dc810d51eb4 100644
--- a/lib/gitlab/ci/variables/collection/item.rb
+++ b/lib/gitlab/ci/variables/collection/item.rb
@@ -7,7 +7,7 @@ module Gitlab
class Item
include Gitlab::Utils::StrongMemoize
- VARIABLES_REGEXP = /\$\$|%%|\$(?<key>[a-zA-Z_][a-zA-Z0-9_]*)|\${\g<key>?}|%\g<key>%/.freeze.freeze
+ VARIABLES_REGEXP = /\$\$|%%|\$(?<key>[a-zA-Z_][a-zA-Z0-9_]*)|\${\g<key>?}|%\g<key>%/
VARIABLE_REF_CHARS = %w[$ %].freeze
def initialize(key:, value:, public: true, file: false, masked: false, raw: false)
diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb
index 7f24b2f78b0..6feaab2a791 100644
--- a/lib/gitlab/cleanup/project_uploads.rb
+++ b/lib/gitlab/cleanup/project_uploads.rb
@@ -93,7 +93,7 @@ module Gitlab
end
class ProjectUploadPath
- PROJECT_FULL_PATH_REGEX = %r{\A#{FileUploader.root}/(.+)/(\h+/[^/]+)\z}.freeze
+ PROJECT_FULL_PATH_REGEX = %r{\A#{FileUploader.root}/(.+)/(\h+/[^/]+)\z}
attr_reader :full_path, :upload_path
diff --git a/lib/gitlab/color.rb b/lib/gitlab/color.rb
index c31c8cb5876..4be78f59bd3 100644
--- a/lib/gitlab/color.rb
+++ b/lib/gitlab/color.rb
@@ -2,7 +2,7 @@
module Gitlab
class Color
- PATTERN = /\A\#(?:[0-9A-Fa-f]{3}){1,2}\Z/.freeze
+ PATTERN = /\A\#(?:[0-9A-Fa-f]{3}){1,2}\Z/
def initialize(value)
@value = value&.strip&.freeze
diff --git a/lib/gitlab/config/loader/multi_doc_yaml.rb b/lib/gitlab/config/loader/multi_doc_yaml.rb
index 084d32a85bc..5db1cc9a5d5 100644
--- a/lib/gitlab/config/loader/multi_doc_yaml.rb
+++ b/lib/gitlab/config/loader/multi_doc_yaml.rb
@@ -6,7 +6,7 @@ module Gitlab
class MultiDocYaml
include Gitlab::Utils::StrongMemoize
- MULTI_DOC_DIVIDER = /^---\s+/.freeze
+ MULTI_DOC_DIVIDER = /^---\s+/
def initialize(config, max_documents:, additional_permitted_classes: [], reject_empty: false)
@config = config
diff --git a/lib/gitlab/database/partitioning.rb b/lib/gitlab/database/partitioning.rb
index 5e6f7024f00..d6cb9d25728 100644
--- a/lib/gitlab/database/partitioning.rb
+++ b/lib/gitlab/database/partitioning.rb
@@ -20,13 +20,13 @@ module Gitlab
registered_tables.merge(tables)
end
- def sync_partitions_ignore_db_error
- sync_partitions unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
+ def sync_partitions_ignore_db_error(analyze: false)
+ sync_partitions(analyze: analyze) unless ENV['DISABLE_POSTGRES_PARTITION_CREATION_ON_STARTUP']
rescue ActiveRecord::ActiveRecordError, PG::Error
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
end
- def sync_partitions(models_to_sync = registered_for_sync, only_on: nil)
+ def sync_partitions(models_to_sync = registered_for_sync, only_on: nil, analyze: true)
return if Feature.enabled?(:disallow_database_ddl_feature_flags, type: :ops)
return unless Feature.enabled?(:partition_manager_sync_partitions, type: :ops)
@@ -34,7 +34,7 @@ module Gitlab
Gitlab::AppLogger.info(message: 'Syncing dynamic postgres partitions')
Gitlab::Database::EachDatabase.each_model_connection(models_to_sync, only_on: only_on) do |model|
- PartitionManager.new(model).sync_partitions
+ PartitionManager.new(model).sync_partitions(analyze: analyze)
end
unless only_on
@@ -44,7 +44,7 @@ module Gitlab
model_connection_name = model.connection_db_config.name
Gitlab::Database::EachDatabase.each_connection(include_shared: false) do |connection, connection_name|
if connection_name != model_connection_name
- PartitionManager.new(model, connection: connection).sync_partitions
+ PartitionManager.new(model, connection: connection).sync_partitions(analyze: analyze)
end
end
end
diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb
index 3fc694beeff..cc5c49cc24a 100644
--- a/lib/gitlab/database/partitioning/partition_manager.rb
+++ b/lib/gitlab/database/partitioning/partition_manager.rb
@@ -19,7 +19,7 @@ module Gitlab
@connection_name = @connection.pool.db_config.name
end
- def sync_partitions
+ def sync_partitions(analyze: true)
return skip_synching_partitions unless table_partitioned?
Gitlab::AppLogger.info(
@@ -37,7 +37,7 @@ module Gitlab
create(partitions_to_create) unless partitions_to_create.empty?
detach(partitions_to_detach) unless partitions_to_detach.empty?
- run_analyze_on_partitioned_table
+ run_analyze_on_partitioned_table if analyze
end
rescue ArgumentError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
@@ -156,26 +156,30 @@ module Gitlab
return if Feature.disabled?(:database_analyze_on_partitioned_tables)
return if ineligible_for_analyzing?
- set_analyze_statement_timeout do
+ primary_transaction(statement_timeout: STATEMENT_TIMEOUT) do
# Running ANALYZE on partitioned table will go through itself and its partitions
connection.execute("ANALYZE VERBOSE #{model.quoted_table_name}")
end
end
def ineligible_for_analyzing?
- first_model_partition.blank? || analyze_interval.blank? || last_analyzed_at_within_interval?
+ analyze_interval.blank? ||
+ first_model_partition.blank? ||
+ last_analyzed_at_within_interval?
end
def last_analyzed_at_within_interval?
table_to_query = first_model_partition.identifier
- # We don't need to get the last_analyze_time from partitioned table,
- # because it's not supported and always returns NULL for PG version below 14
- # Therefore, we can always get the last_analyze_time from the first partition
- last_analyzed_at = connection.select_value(
- "SELECT pg_stat_get_last_analyze_time('#{table_to_query}'::regclass)"
- )
- last_analyzed_at.present? && last_analyzed_at >= Time.current - analyze_interval
+ primary_transaction do
+ # We don't need to get the last_analyze_time from partitioned table,
+ # because it's not supported and always returns NULL for PG version below 14
+ # Therefore, we can always get the last_analyze_time from the first partition
+ last_analyzed_at = connection.select_value(
+ "SELECT pg_stat_get_last_analyze_time('#{table_to_query}'::regclass)"
+ )
+ last_analyzed_at.present? && last_analyzed_at >= Time.current - analyze_interval
+ end
end
def first_model_partition
@@ -189,11 +193,18 @@ module Gitlab
model.partitioning_strategy.analyze_interval
end
- def set_analyze_statement_timeout
- connection.execute(format("SET statement_timeout TO '%ds'", STATEMENT_TIMEOUT))
- yield
- ensure
- connection.execute('RESET statement_timeout')
+ def primary_transaction(statement_timeout: nil)
+ Gitlab::Database::LoadBalancing::Session.current.use_primary do
+ connection.transaction(requires_new: false) do
+ if statement_timeout.present?
+ connection.execute(
+ format("SET LOCAL statement_timeout TO '%ds'", statement_timeout)
+ )
+ end
+
+ yield
+ end
+ end
end
end
end
diff --git a/lib/gitlab/github_import/object_counter.rb b/lib/gitlab/github_import/object_counter.rb
index 1e92b58798b..88e91800cee 100644
--- a/lib/gitlab/github_import/object_counter.rb
+++ b/lib/gitlab/github_import/object_counter.rb
@@ -38,7 +38,7 @@ module Gitlab
# After import is completed we store this information in project's import_checksums
return cached_summary if cached_summary != EMPTY_SUMMARY || project.import_state.blank?
- project.import_state.in_progress? ? cached_summary : project.import_checksums
+ project.import_state.completed? ? project.import_checksums : cached_summary
end
private
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3d96403c8cf..0ed1810f1a7 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -27596,9 +27596,6 @@ msgstr ""
msgid "Learn more about shards and replicas in the %{configuration_link_start}Advanced Search configuration%{configuration_link_end} documentation. Changes don't take place until you %{recreated_link_start}recreate%{recreated_link_end} the index."
msgstr ""
-msgid "Learn more in the"
-msgstr ""
-
msgid "Learn more."
msgstr ""
@@ -34241,7 +34238,13 @@ msgstr ""
msgid "PipelineGraph|What is a downstream pipeline?"
msgstr ""
-msgid "PipelineScheduleIntervalPattern|Custom (%{linkStart}Learn more%{linkEnd}.)"
+msgid "PipelineScheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "PipelineScheduleIntervalPattern|Set a custom interval with Cron syntax."
+msgstr ""
+
+msgid "PipelineScheduleIntervalPattern|What is Cron syntax?"
msgstr ""
msgid "PipelineSchedules|A scheduled pipeline starts automatically at regular intervals, like daily or weekly. The pipeline: "
@@ -36866,6 +36869,9 @@ msgstr ""
msgid "ProjectSettings|Allow anyone to pull from Package Registry"
msgstr ""
+msgid "ProjectSettings|Allow skipping the merge train"
+msgstr ""
+
msgid "ProjectSettings|Always show thumbs-up and thumbs-down emoji buttons on issues, merge requests, and snippets."
msgstr ""
@@ -37085,6 +37091,9 @@ msgstr ""
msgid "ProjectSettings|Merge requests approved for merge are queued, and pipelines validate the combined results of the source and target branches before merge. %{link_start}What are merge trains?%{link_end}"
msgstr ""
+msgid "ProjectSettings|Merge requests can be set to merge immediately without interrupting the merge train. Commits in earlier merge train pipelines might not get validated with immediately merged commits."
+msgstr ""
+
msgid "ProjectSettings|Merge suggestions"
msgstr ""
@@ -41667,9 +41676,6 @@ msgstr ""
msgid "Schedules to merge this merge request (%{strategy})."
msgstr ""
-msgid "Scheduling Pipelines"
-msgstr ""
-
msgid "Scope"
msgstr ""
@@ -47529,9 +47535,6 @@ msgstr ""
msgid "The pipeline has been deleted"
msgstr ""
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr ""
-
msgid "The project has already been added to your dashboard."
msgstr ""
@@ -56929,9 +56932,6 @@ msgstr ""
msgid "pipeline"
msgstr ""
-msgid "pipeline schedules documentation"
-msgstr ""
-
msgid "pipelineEditorWalkthrough|Let's do this!"
msgstr ""
diff --git a/package.json b/package.json
index 6a1ab1bc288..e9e20d439e6 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"@gitlab/cluster-client": "^1.3.0",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
- "@gitlab/svgs": "3.62.0",
+ "@gitlab/svgs": "3.63.0",
"@gitlab/ui": "66.4.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230915130935",
diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
index f5a7dfe6d11..50d09481b93 100644
--- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
+++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
@@ -1,6 +1,5 @@
-import { GlIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
@@ -21,15 +20,15 @@ describe('Interval Pattern Input Component', () => {
const everyDayKey = 'everyDay';
const cronIntervalNotInPreset = `0 12 * * *`;
- const findEveryDayRadio = () => wrapper.find(`[data-testid=${everyDayKey}]`);
- const findEveryWeekRadio = () => wrapper.find('[data-testid="everyWeek"]');
- const findEveryMonthRadio = () => wrapper.find('[data-testid="everyMonth"]');
- const findCustomRadio = () => wrapper.find(`[data-testid="${customKey}"]`);
+ const findEveryDayRadio = () => wrapper.findByTestId(everyDayKey);
+ const findEveryWeekRadio = () => wrapper.findByTestId('everyWeek');
+ const findEveryMonthRadio = () => wrapper.findByTestId('everyMonth');
+ const findCustomRadio = () => wrapper.findByTestId(customKey);
const findCustomInput = () => wrapper.find('#schedule_cron');
const findAllLabels = () => wrapper.findAll('label');
const findSelectedRadio = () =>
wrapper.findAll('input[type="radio"]').wrappers.find((x) => x.element.checked);
- const findIcon = () => wrapper.findComponent(GlIcon);
+ const findIcon = () => wrapper.findByTestId('daily-limit');
const findSelectedRadioKey = () => findSelectedRadio()?.attributes('data-testid');
const selectEveryDayRadio = () => findEveryDayRadio().setChecked(true);
const selectEveryWeekRadio = () => findEveryWeekRadio().setChecked(true);
@@ -37,7 +36,7 @@ describe('Interval Pattern Input Component', () => {
const selectCustomRadio = () => findCustomRadio().setChecked(true);
const createWrapper = (props = {}, data = {}) => {
- wrapper = mount(IntervalPatternInput, {
+ wrapper = mountExtended(IntervalPatternInput, {
propsData: { ...props },
data() {
return {
@@ -132,7 +131,7 @@ describe('Interval Pattern Input Component', () => {
'Every day (at 4:00am)',
'Every week (Monday at 4:00am)',
'Every month (Day 1 at 4:00am)',
- 'Custom (Learn more.)',
+ 'Custom',
]);
});
});
diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
deleted file mode 100644
index e20c2fa47a7..00000000000
--- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
+++ /dev/null
@@ -1,92 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import Cookies from '~/lib/utils/cookies';
-import PipelineSchedulesCallout from '~/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue';
-
-const cookieKey = 'pipeline_schedules_callout_dismissed';
-const docsUrl = 'help/ci/scheduled_pipelines';
-const illustrationUrl = 'pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg';
-
-describe('Pipeline Schedule Callout', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineSchedulesCallout, {
- provide: {
- docsUrl,
- illustrationUrl,
- },
- });
- };
-
- const findInnerContentOfCallout = () => wrapper.find('[data-testid="innerContent"]');
- const findDismissCalloutBtn = () => wrapper.findComponent(GlButton);
-
- describe(`when ${cookieKey} cookie is set`, () => {
- beforeEach(async () => {
- Cookies.set(cookieKey, true);
- createComponent();
-
- await nextTick();
- });
-
- it('does not render the callout', () => {
- expect(findInnerContentOfCallout().exists()).toBe(false);
- });
- });
-
- describe('when cookie is not set', () => {
- beforeEach(() => {
- Cookies.remove(cookieKey);
- createComponent();
- });
-
- it('renders the callout container', () => {
- expect(findInnerContentOfCallout().exists()).toBe(true);
- });
-
- it('renders the callout title', () => {
- expect(wrapper.find('h4').text()).toBe('Scheduling Pipelines');
- });
-
- it('renders the callout text', () => {
- expect(wrapper.find('p').text()).toContain('runs pipelines in the future');
- });
-
- it('renders the documentation url', () => {
- expect(wrapper.find('a').attributes('href')).toBe(docsUrl);
- });
-
- describe('methods', () => {
- it('#dismissCallout sets calloutDismissed to true', async () => {
- expect(wrapper.vm.calloutDismissed).toBe(false);
-
- findDismissCalloutBtn().vm.$emit('click');
-
- await nextTick();
-
- expect(findInnerContentOfCallout().exists()).toBe(false);
- });
-
- it('sets cookie on dismiss', () => {
- const setCookiesSpy = jest.spyOn(Cookies, 'set');
-
- findDismissCalloutBtn().vm.$emit('click');
-
- expect(setCookiesSpy).toHaveBeenCalledWith('pipeline_schedules_callout_dismissed', true, {
- expires: 365,
- secure: false,
- });
- });
- });
-
- it('is hidden when close button is clicked', async () => {
- findDismissCalloutBtn().vm.$emit('click');
-
- await nextTick();
-
- expect(findInnerContentOfCallout().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb b/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb
index 6d8e15ac791..93456ec7b93 100644
--- a/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb
+++ b/spec/graphql/mutations/users/set_namespace_commit_email_spec.rb
@@ -71,5 +71,5 @@ RSpec.describe Mutations::Users::SetNamespaceCommitEmail, feature_category: :use
end
end
- specify { expect(described_class).to require_graphql_authorizations(:read_namespace) }
+ specify { expect(described_class).to require_graphql_authorizations(:read_namespace_via_membership) }
end
diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb
index d80235023ef..9e1a2bfd466 100644
--- a/spec/graphql/types/namespace_type_spec.rb
+++ b/spec/graphql/types/namespace_type_spec.rb
@@ -15,5 +15,5 @@ RSpec.describe GitlabSchema.types['Namespace'] do
expect(described_class).to include_graphql_fields(*expected_fields)
end
- specify { expect(described_class).to require_graphql_authorizations(:read_namespace) }
+ specify { expect(described_class).to require_graphql_authorizations(:read_namespace_via_membership) }
end
diff --git a/spec/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..3dbb1b34726
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_finding_id_in_vulnerabilities_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+RSpec.describe Gitlab::BackgroundMigration::BackfillFindingIdInVulnerabilities, schema: 20230912105945, feature_category: :vulnerability_management do # rubocop:disable Layout/LineLength
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:users) { table(:users) }
+ let(:members) { table(:members) }
+ let(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let(:vulnerability_scanners) { table(:vulnerability_scanners) }
+ let(:vulnerability_findings) { table(:vulnerability_occurrences) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+ let!(:user) { create_user(email: "test1@example.com", username: "test1") }
+ let!(:namespace) { namespaces.create!(name: "test-1", path: "test-1", owner_id: user.id) }
+ let!(:project) do
+ projects.create!(
+ id: 9999, namespace_id: namespace.id,
+ project_namespace_id: namespace.id,
+ creator_id: user.id
+ )
+ end
+
+ let!(:membership) do
+ members.create!(access_level: 50, source_id: project.id, source_type: "Project", user_id: user.id, state: 0,
+ notification_level: 3, type: "ProjectMember", member_namespace_id: namespace.id)
+ end
+
+ let(:migration_attrs) do
+ {
+ start_id: vulnerabilities.first.id,
+ end_id: vulnerabilities.last.id,
+ batch_table: :vulnerabilities,
+ batch_column: :id,
+ sub_batch_size: 100,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ }
+ end
+
+ describe "#perform" do
+ subject(:background_migration) { described_class.new(**migration_attrs).perform }
+
+ # This scenario is what usually happens because we first create a Vulnerabilities::Finding record, then create
+ # a Vulnerability record and populate the Vulnerabilities::Finding#vulnerability_id
+ let(:vulnerabilities_finding_1) { create_finding(project, vulnerability_id: vulnerability_without_finding_id.id) }
+ let(:vulnerability_without_finding_id) { create_vulnerability }
+
+ # This scenario can occur because we have modified our Vulnerabilities ingestion pipeline to populate
+ # vulnerabilities.finding_id as soon as possible
+ let(:vulnerabilities_finding_2) { create_finding(project) }
+ let(:vulnerability_with_finding_id) { create_vulnerability(finding_id: vulnerabilities_finding_2.id) }
+
+ it 'backfills finding_id column in the vulnerabilities table' do
+ expect { background_migration }.to change { vulnerability_without_finding_id.reload.finding_id }
+ .from(nil).to(vulnerabilities_finding_1.id)
+ end
+
+ it 'does not affect rows with finding_id populated' do
+ expect { background_migration }.not_to change { vulnerability_with_finding_id.reload.finding_id }
+ end
+ end
+
+ private
+
+ def create_scanner(project, overrides = {})
+ attrs = {
+ project_id: project.id,
+ external_id: "test_vulnerability_scanner",
+ name: "Test Vulnerabilities::Scanner"
+ }.merge(overrides)
+
+ vulnerability_scanners.create!(attrs)
+ end
+
+ def create_identifier(project, overrides = {})
+ attrs = {
+ project_id: project.id,
+ external_id: "CVE-2018-1234",
+ external_type: "CVE",
+ name: "CVE-2018-1234",
+ fingerprint: SecureRandom.hex(20)
+ }.merge(overrides)
+
+ vulnerability_identifiers.create!(attrs)
+ end
+
+ def create_finding(project, overrides = {})
+ attrs = {
+ project_id: project.id,
+ scanner_id: create_scanner(project).id,
+ severity: 5, # medium
+ confidence: 2, # unknown,
+ report_type: 99, # generic
+ primary_identifier_id: create_identifier(project).id,
+ project_fingerprint: SecureRandom.hex(20),
+ location_fingerprint: SecureRandom.hex(20),
+ uuid: SecureRandom.uuid,
+ name: "CVE-2018-1234",
+ raw_metadata: "{}",
+ metadata_version: "test:1.0"
+ }.merge(overrides)
+
+ vulnerability_findings.create!(attrs)
+ end
+
+ def create_vulnerability(overrides = {})
+ attrs = {
+ project_id: project.id,
+ author_id: user.id,
+ title: 'test',
+ severity: 1,
+ confidence: 1,
+ report_type: 1,
+ state: 1,
+ detected_at: Time.zone.now
+ }.merge(overrides)
+
+ vulnerabilities.create!(attrs)
+ end
+
+ def create_user(overrides = {})
+ attrs = {
+ email: "test@example.com",
+ notification_email: "test@example.com",
+ name: "test",
+ username: "test",
+ state: "active",
+ projects_limit: 10
+ }.merge(overrides)
+
+ users.create!(attrs)
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
index f2c309e8e88..c41228777ca 100644
--- a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
let(:connection) { ActiveRecord::Base.connection }
let(:table) { partitioned_table_name }
let(:partitioning_strategy) do
- double(missing_partitions: partitions, extra_partitions: [], after_adding_partitions: nil)
+ double(missing_partitions: partitions, extra_partitions: [], after_adding_partitions: nil, analyze_interval: nil)
end
let(:partitions) do
@@ -126,7 +126,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
let(:connection) { ActiveRecord::Base.connection }
let(:table) { :_test_foo }
let(:partitioning_strategy) do
- double(extra_partitions: extra_partitions, missing_partitions: [], after_adding_partitions: nil)
+ double(extra_partitions: extra_partitions, missing_partitions: [], after_adding_partitions: nil, analyze_interval: nil)
end
before do
@@ -258,6 +258,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
end
describe 'analyze partitioned table' do
+ let(:analyze) { true }
let(:analyze_table) { partitioned_table_name }
let(:analyze_partition) { "#{partitioned_table_name}_1" }
let(:analyze_regex) { /ANALYZE VERBOSE "#{analyze_table}"/ }
@@ -278,30 +279,30 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
end
shared_examples_for 'run only once analyze within interval' do
- it 'runs only once analyze within interval' do
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ specify do
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).to include(analyze_regex)
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).not_to include(analyze_regex)
- travel_to((analyze_interval * 1.1).since) do
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ travel_to((analyze_interval * 2).since) do
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).to include(analyze_regex)
end
end
end
shared_examples_for 'not to run the analyze at all' do
- it 'does not run the analyze at all' do
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ specify do
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).not_to include(analyze_regex)
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).not_to include(analyze_regex)
travel_to((analyze_interval * 2).since) do
- control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions }
+ control = ActiveRecord::QueryRecorder.new { described_class.new(my_model, connection: connection).sync_partitions(analyze: analyze) }
expect(control.occurrences).not_to include(analyze_regex)
end
end
@@ -328,6 +329,12 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
it_behaves_like 'run only once analyze within interval'
+ context 'when analyze is false' do
+ let(:analyze) { false }
+
+ it_behaves_like 'not to run the analyze at all'
+ end
+
context 'when model does not set analyze_interval' do
let(:my_model) do
Class.new(ApplicationRecord) do
@@ -357,6 +364,12 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager, feature_categor
it_behaves_like 'not to run the analyze at all'
+ context 'when analyze is false' do
+ let(:analyze) { false }
+
+ it_behaves_like 'not to run the analyze at all'
+ end
+
context 'when model does not set analyze_interval' do
let(:my_model) do
Class.new(ApplicationRecord) do
diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb
index b6b7d293778..e53e0cb8def 100644
--- a/spec/lib/gitlab/database/partitioning_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Gitlab::Database::Partitioning, feature_category: :database do
describe '.sync_partitions_ignore_db_error' do
it 'calls sync_partitions' do
- expect(described_class).to receive(:sync_partitions)
+ expect(described_class).to receive(:sync_partitions).with(analyze: false)
described_class.sync_partitions_ignore_db_error
end
@@ -104,6 +104,55 @@ RSpec.describe Gitlab::Database::Partitioning, feature_category: :database do
.and change { find_partitions(table_names.last).size }.from(0)
end
+ context 'for analyze' do
+ let(:analyze_regex) { /ANALYZE VERBOSE / }
+ let(:analyze) { true }
+
+ shared_examples_for 'not running analyze' do
+ specify do
+ control = ActiveRecord::QueryRecorder.new { described_class.sync_partitions(analyze: analyze) }
+ expect(control.occurrences).not_to include(analyze_regex)
+ end
+ end
+
+ context 'when analyze_interval is not set' do
+ it_behaves_like 'not running analyze'
+
+ context 'when analyze is set to false' do
+ it_behaves_like 'not running analyze'
+ end
+ end
+
+ context 'when analyze_interval is set' do
+ let(:models) do
+ [
+ Class.new(ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = :_test_partitioning_test1
+ partitioned_by :created_at, strategy: :monthly, analyze_interval: 1.week
+ end,
+ Class.new(Gitlab::Database::Partitioning::TableWithoutModel).tap do |klass|
+ klass.table_name = :_test_partitioning_test2
+ klass.partitioned_by(:created_at, strategy: :monthly, analyze_interval: 1.week)
+ klass.limit_connection_names = %i[main]
+ end
+ ]
+ end
+
+ it 'runs analyze' do
+ control = ActiveRecord::QueryRecorder.new { described_class.sync_partitions(models, analyze: analyze) }
+ expect(control.occurrences).to include(analyze_regex)
+ end
+
+ context 'analyze is false' do
+ let(:analyze) { false }
+
+ it_behaves_like 'not running analyze'
+ end
+ end
+ end
+
context 'with multiple databases' do
it 'creates partitions in each database' do
skip_if_shared_database(:ci)
diff --git a/spec/migrations/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion_spec.rb b/spec/migrations/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion_spec.rb
new file mode 100644
index 00000000000..01dbb5d1ef8
--- /dev/null
+++ b/spec/migrations/20230906204934_restart_self_hosted_sent_notifications_bigint_conversion_spec.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+def column_type_from_table(table, column)
+ table.columns.find { |c| c.name == column }.sql_type
+end
+
+RSpec.describe RestartSelfHostedSentNotificationsBigintConversion, feature_category: :database do
+ let(:sent_notifications) { table(:sent_notifications) }
+
+ before do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(!self_hosted)
+ # rubocop: enable RSpec/AnyInstanceOf
+ end
+
+ context 'when is self-hosted' do
+ let(:self_hosted) { true }
+
+ describe '#up' do
+ context 'when id is already a bigint' do
+ it 'does nothing' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ sent_notifications.reset_column_information
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('bigint')
+ }
+ migration.after -> {
+ sent_notifications.reset_column_information
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('bigint')
+ }
+ end
+ end
+ end
+ end
+
+ context 'when id is an integer and id_convert_to_bigint exists' do
+ before do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE integer')
+ conn.execute('ALTER TABLE sent_notifications ADD COLUMN id_convert_to_bigint BIGINT')
+ sent_notifications.reset_column_information
+ end
+
+ after do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE bigint')
+ conn.execute('ALTER TABLE sent_notifications DROP COLUMN id_convert_to_bigint')
+ sent_notifications.reset_column_information
+ end
+
+ it 'does nothing' do
+ disable_migrations_output do
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('integer')
+ expect(sent_notifications.columns.find { |c| c.name == 'id_convert_to_bigint' }).not_to be_nil
+ migrate!
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('integer')
+ expect(sent_notifications.columns.find { |c| c.name == 'id_convert_to_bigint' }).not_to be_nil
+ end
+ end
+ end
+
+ context 'when id is an integer and id_convert_to_bigint does not exist' do
+ before do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE integer')
+ conn.execute('ALTER TABLE sent_notifications DROP COLUMN IF EXISTS id_convert_to_bigint')
+ sent_notifications.reset_column_information
+ end
+
+ after do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE bigint')
+ conn.execute('ALTER TABLE sent_notifications DROP COLUMN IF EXISTS id_convert_to_bigint')
+ sent_notifications.reset_column_information
+ end
+
+ it 'creates id_convert_to_bigint' do
+ disable_migrations_output do
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('integer')
+ expect(sent_notifications.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be_nil
+ migrate!
+ sent_notifications.reset_column_information
+ expect(column_type_from_table(sent_notifications, 'id')).to eq('integer')
+ expect(sent_notifications.columns.find { |c| c.name == 'id_convert_to_bigint' }).not_to be_nil
+ end
+ end
+ end
+ end
+
+ describe '#down' do
+ context 'when id is an integer and id_convert_to_bigint exists' do
+ before do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE integer')
+ conn.execute('ALTER TABLE sent_notifications ADD COLUMN id_convert_to_bigint BIGINT')
+ sent_notifications.reset_column_information
+ end
+
+ after do
+ conn = described_class.new.connection
+ conn.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE bigint')
+ conn.execute('ALTER TABLE sent_notifications DROP COLUMN IF EXISTS id_convert_to_bigint')
+ sent_notifications.reset_column_information
+ end
+
+ it 'drops id_convert_to_bigint' do
+ disable_migrations_output do
+ migrate!
+ schema_migrate_down!
+ end
+ expect(sent_notifications.columns.find { |c| c.name == 'id_convert_to_bigint' }).to be_nil
+ end
+ end
+ end
+ end
+
+ context 'when is not self-hosted' do
+ let(:self_hosted) { false }
+
+ describe '#up' do
+ it 'is a bigint and result in no change' do
+ disable_migrations_output do
+ reversible_migration do |migration|
+ migration.before -> {
+ sent_notifications.reset_column_information
+ expect(sent_notifications.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
+ }
+ migration.after -> {
+ sent_notifications.reset_column_information
+ expect(sent_notifications.columns.find { |c| c.name == 'id' }.sql_type).to eq('bigint')
+ }
+ end
+ end
+ end
+ end
+
+ # Do not need to describe #down since it's a no-op and we did reversible test above
+ end
+end
diff --git a/spec/migrations/20230906204935_restart_self_hosted_sent_notifications_backfill_spec.rb b/spec/migrations/20230906204935_restart_self_hosted_sent_notifications_backfill_spec.rb
new file mode 100644
index 00000000000..f2c9ce3d005
--- /dev/null
+++ b/spec/migrations/20230906204935_restart_self_hosted_sent_notifications_backfill_spec.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+def column_type_from_table(table, column)
+ table.columns.find { |c| c.name == column }.sql_type
+end
+
+def sent_notifications_backfills(connection)
+ res = connection.execute <<~SQL
+ SELECT * FROM batched_background_migrations WHERE table_name = 'sent_notifications'
+ SQL
+
+ res.ntuples
+end
+
+def create_previous_backfill(connection)
+ connection.execute <<~SQL
+ INSERT INTO batched_background_migrations
+ (min_value, max_value, batch_size, sub_batch_size, interval, "status",#{' '}
+ job_class_name, batch_class_name,
+ table_name, column_name, job_arguments,
+ gitlab_schema, created_at, updated_at)
+ VALUES
+ (1, 3, 20000, 1000, 120, 3,
+ 'CopyColumnUsingBackgroundMigrationJob', 'PrimaryKeyBatchingStrategy',
+ 'sent_notifications', 'id', '[["id"], ["id_convert_to_bigint"]]',
+ 'gitlab_main', NOW(), NOW())
+ SQL
+end
+
+RSpec.describe RestartSelfHostedSentNotificationsBackfill, feature_category: :database do
+ let(:sent_notifications) { table(:sent_notifications) }
+
+ before do
+ # rubocop: disable RSpec/AnyInstanceOf
+ allow_any_instance_of(described_class).to receive(:com_or_dev_or_test_but_not_jh?).and_return(!self_hosted)
+ # rubocop: enable RSpec/AnyInstanceOf
+ end
+
+ describe '#up' do
+ context 'when is self-hosted' do
+ let(:self_hosted) { true }
+
+ context 'when id is integer' do
+ before do
+ described_class.new.connection.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE integer')
+ described_class.new.connection.execute(
+ 'ALTER TABLE sent_notifications ADD COLUMN IF NOT EXISTS id_convert_to_bigint BIGINT'
+ )
+ sent_notifications.reset_column_information
+ end
+
+ after do
+ described_class.new.connection.execute('ALTER TABLE sent_notifications ALTER COLUMN id TYPE bigint')
+ described_class.new.connection.execute(
+ 'ALTER TABLE sent_notifications DROP COLUMN IF EXISTS id_convert_to_bigint'
+ )
+ sent_notifications.reset_column_information
+ end
+
+ context 'when a backfill has never been done' do
+ let(:id_convert_to_bigint_sample) { 0 }
+
+ before do
+ described_class.new.connection.execute <<~SQL
+ INSERT INTO
+ sent_notifications
+ (id_convert_to_bigint, reply_key)
+ VALUES (#{id_convert_to_bigint_sample}, 4)
+ SQL
+ end
+
+ after do
+ described_class.new.connection.execute <<~SQL
+ DELETE FROM sent_notifications
+ SQL
+ end
+
+ context 'when there is a record of an incomplete backfill' do
+ before do
+ create_previous_backfill(described_class.new.connection)
+ end
+
+ after do
+ described_class.new.connection.execute <<~SQL
+ DELETE FROM batched_background_migrations
+ SQL
+ end
+
+ it 'calls delete_batched_background_migration and does not raise an error' do
+ expect_next_instance_of(described_class) do |instance|
+ expect(instance).to receive(:delete_batched_background_migration)
+ end
+ disable_migrations_output do
+ expect { migrate! }.not_to raise_error
+ end
+ expect(sent_notifications_backfills(described_class.new.connection)).to eq 1
+ end
+ end
+
+ context 'when there is no previous record of a backfill' do
+ it 'begins a backfill' do
+ disable_migrations_output do
+ migrate!
+ end
+ expect(sent_notifications_backfills(described_class.new.connection)).to eq 1
+ end
+ end
+ end
+
+ context 'when a backfill has previously been done' do
+ let(:id_convert_to_bigint_sample) { 4 }
+
+ before do
+ described_class.new.connection.execute <<~SQL
+ INSERT INTO
+ sent_notifications
+ (id_convert_to_bigint, reply_key)
+ VALUES (#{id_convert_to_bigint_sample}, 4)
+ SQL
+ end
+
+ after do
+ described_class.new.connection.execute <<~SQL
+ DELETE FROM sent_notifications
+ SQL
+ end
+
+ it 'does not start a backfill' do
+ disable_migrations_output do
+ migrate!
+ end
+ expect(sent_notifications_backfills(described_class.new.connection)).to eq 0
+ end
+ end
+ end
+
+ context 'when id is a bigint' do
+ it 'does not start a backfill' do
+ disable_migrations_output do
+ migrate!
+ end
+ expect(sent_notifications_backfills(described_class.new.connection)).to eq 0
+ end
+ end
+ end
+
+ context 'when is not self-hosted' do
+ let(:self_hosted) { false }
+
+ it 'does not start a backfill' do
+ disable_migrations_output do
+ migrate!
+ end
+ expect(sent_notifications_backfills(described_class.new.connection)).to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230912105945_queue_backfill_finding_id_in_vulnerabilities_spec.rb b/spec/migrations/20230912105945_queue_backfill_finding_id_in_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..02c39408d40
--- /dev/null
+++ b/spec/migrations/20230912105945_queue_backfill_finding_id_in_vulnerabilities_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueueBackfillFindingIdInVulnerabilities, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :vulnerabilities,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+end
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index 7ceb4931c4f..10f4791b216 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -125,6 +125,14 @@ RSpec.describe ProjectImportState, type: :model, feature_category: :importers do
end
end
+ describe '#completed?' do
+ it { expect(described_class.new(status: :failed)).to be_completed }
+ it { expect(described_class.new(status: :finished)).to be_completed }
+ it { expect(described_class.new(status: :canceled)).to be_completed }
+ it { expect(described_class.new(status: :scheduled)).not_to be_completed }
+ it { expect(described_class.new(status: :started)).not_to be_completed }
+ end
+
describe '#expire_etag_cache' do
context 'when project import type has realtime changes endpoint' do
before do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d2419ab89fc..aedfc7fca53 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1158,6 +1158,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
merge_pipelines_enabled
merge_trains_enabled
auto_rollback_enabled
+ merge_trains_skip_train_allowed
)
end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 4d72de27046..4aa18c72c45 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -19,6 +19,7 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
expect_disallowed(:read_namespace)
+ expect_disallowed(:read_namespace_via_membership)
end
end
@@ -34,6 +35,7 @@ RSpec.describe GroupPolicy, feature_category: :system_access do
expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
expect_disallowed(:read_namespace)
+ expect_disallowed(:read_namespace_via_membership)
end
end
diff --git a/spec/policies/namespaces/user_namespace_policy_spec.rb b/spec/policies/namespaces/user_namespace_policy_spec.rb
index 41555ca4150..b4fbc7e0417 100644
--- a/spec/policies/namespaces/user_namespace_policy_spec.rb
+++ b/spec/policies/namespaces/user_namespace_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Namespaces::UserNamespacePolicy, feature_category: :groups_and_pr
let_it_be(:admin) { create(:admin) }
let_it_be(:namespace) { create(:user_namespace, owner: owner) }
- let(:owner_permissions) { [:owner_access, :create_projects, :admin_namespace, :read_namespace, :read_statistics, :transfer_projects, :admin_package, :read_billing, :edit_billing, :import_projects] }
+ let(:owner_permissions) { [:owner_access, :create_projects, :admin_namespace, :read_namespace, :read_namespace_via_membership, :read_statistics, :transfer_projects, :admin_package, :read_billing, :edit_billing, :import_projects] }
subject { described_class.new(current_user, namespace) }
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 677cb243a7c..d95f96c25d6 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -94,6 +94,7 @@ ci_cd_settings:
- id
- project_id
- merge_trains_enabled
+ - merge_trains_skip_train_allowed
- merge_pipelines_enabled
- auto_rollback_enabled
- inbound_job_token_scope_enabled
diff --git a/spec/serializers/import/github_realtime_repo_entity_spec.rb b/spec/serializers/import/github_realtime_repo_entity_spec.rb
index 7f137366be2..bbaeb5c4ea8 100644
--- a/spec/serializers/import/github_realtime_repo_entity_spec.rb
+++ b/spec/serializers/import/github_realtime_repo_entity_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Import::GithubRealtimeRepoEntity, feature_category: :importers do
subject(:entity) { described_class.new(project) }
- let(:import_state) { instance_double(ProjectImportState, failed?: false, in_progress?: true) }
+ let(:import_state) { instance_double(ProjectImportState, failed?: false, completed?: true) }
let(:import_failures) { [instance_double(ImportFailure, exception_message: 'test error')] }
let(:project) do
instance_double(
@@ -27,7 +27,7 @@ RSpec.describe Import::GithubRealtimeRepoEntity, feature_category: :importers do
end
context 'when import stats is failed' do
- let(:import_state) { instance_double(ProjectImportState, failed?: true, in_progress?: false) }
+ let(:import_state) { instance_double(ProjectImportState, failed?: true, completed?: true) }
it 'includes import_error' do
data = entity.as_json
diff --git a/spec/serializers/import/github_realtime_repo_serializer_spec.rb b/spec/serializers/import/github_realtime_repo_serializer_spec.rb
index b656132e332..825118d0f80 100644
--- a/spec/serializers/import/github_realtime_repo_serializer_spec.rb
+++ b/spec/serializers/import/github_realtime_repo_serializer_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Import::GithubRealtimeRepoSerializer, feature_category: :importer
end
describe '#represent' do
- let(:import_state) { instance_double(ProjectImportState, failed?: false, in_progress?: true) }
+ let(:import_state) { instance_double(ProjectImportState, failed?: false, completed?: false) }
let(:project) do
instance_double(
Project,
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index f6f10d106a3..298f4006c3b 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -590,7 +590,6 @@
- './ee/spec/graphql/api/vulnerabilities_spec.rb'
- './ee/spec/graphql/ee/mutations/boards/issues/issue_move_list_spec.rb'
- './ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
-- './ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb'
- './ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
- './ee/spec/graphql/ee/mutations/concerns/mutations/resolves_issuable_spec.rb'
- './ee/spec/graphql/ee/resolvers/board_list_issues_resolver_spec.rb'
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 70b48322efd..4564fa23236 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -19,7 +19,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:guest_permissions) do
%i[
- read_label read_group upload_file read_namespace read_group_activity
+ read_label read_group upload_file read_namespace read_namespace_via_membership read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones
read_group_merge_requests
]
diff --git a/yarn.lock b/yarn.lock
index 248442e10d2..ef68193fbf0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1269,10 +1269,10 @@
stylelint-declaration-strict-value "1.9.2"
stylelint-scss "5.1.0"
-"@gitlab/svgs@3.62.0":
- version "3.62.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.62.0.tgz#9b18a451d7f53d49db859a17eff909248c80c6b5"
- integrity sha512-fnctTGTKgqFQxOFpYn0cklp87pZUj9bWLUAcznUJZJUzGXFFz9OrpcQWHMwP1QRDh/SUym2uWlEiJ6J7wFF9RA==
+"@gitlab/svgs@3.63.0":
+ version "3.63.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.63.0.tgz#48e41f50e6b03bcd065eafebf44b8c0f23de3df3"
+ integrity sha512-rmEljjWhF7iieTjdx2edcsbSqgnW6AdD5Ou37p+cdlIll3lCcK85HpB5Kq474FNLCGoyTaVtnwpURBbWQMv/cg==
"@gitlab/ui@66.4.0":
version "66.4.0"