diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-20 09:11:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-20 09:11:22 +0300 |
commit | 13ad005a25f163520ff94b90cdd53495c5a0b5c4 (patch) | |
tree | 5b2fb4262633c200a8e701de2d1fa97105641ed2 | |
parent | 9a61b4604efea1c8e57f4c90addbc94ecbe874de (diff) |
Add latest changes from gitlab-org/gitlab@master
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" |