diff --git a/.gitlab/ci/qa-common/main.gitlab-ci.yml b/.gitlab/ci/qa-common/main.gitlab-ci.yml index 80ea4a10f2b..d660851e2ee 100644 --- a/.gitlab/ci/qa-common/main.gitlab-ci.yml +++ b/.gitlab/ci/qa-common/main.gitlab-ci.yml @@ -5,7 +5,7 @@ workflow: name: $PIPELINE_NAME include: - - component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@8.15.0" + - component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@8.15.1" inputs: job_name: "e2e-test-report" job_stage: "report" @@ -15,7 +15,7 @@ include: gitlab_auth_token_variable_name: "PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE" allure_job_name: "${QA_RUN_TYPE}" - project: gitlab-org/quality/pipeline-common - ref: 8.15.0 + ref: 8.15.1 file: - /ci/base.gitlab-ci.yml - /ci/knapsack-report.yml @@ -44,6 +44,12 @@ stages: BUNDLE_SILENCE_ROOT_WARNING: "true" BUNDLE_WITHOUT: development +.qa-run-e2e-with-bundler: + script: + - source $CI_PROJECT_DIR/scripts/utils.sh + - source $CI_PROJECT_DIR/scripts/rspec_helpers.sh + - run_e2e_specs "${QA_GITLAB_URL}" "${QA_TESTS}" "${QA_RSPEC_TAGS}" + .qa-install: extends: - .bundler-variables diff --git a/.gitlab/ci/qa-common/variables.gitlab-ci.yml b/.gitlab/ci/qa-common/variables.gitlab-ci.yml index a47be5f5a91..bd99a455f40 100644 --- a/.gitlab/ci/qa-common/variables.gitlab-ci.yml +++ b/.gitlab/ci/qa-common/variables.gitlab-ci.yml @@ -16,4 +16,4 @@ variables: QA_OMNIBUS_MR_TESTS: "only-smoke" # Retry failed specs in separate process QA_RETRY_FAILED_SPECS: "true" - GITLAB_HELM_CHART_REF: "d1eaee3922df2bfbdc23b55720965c10eec2b93d" # helm chart ref used by test-on-cng pipeline + GITLAB_HELM_CHART_REF: "c7532b6e1ba98d5663b58012a879a781689db916" # helm chart ref used by test-on-cng pipeline diff --git a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml index db70200078a..c7def392993 100644 --- a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml @@ -13,7 +13,9 @@ workflow: .cng-base: image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/${BUILD_OS}-${OS_VERSION}-ruby-${RUBY_VERSION}:bundler-${BUNDLER_VERSION}-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-kubectl-1.23-helm-3.14-kind-0.20 stage: test - extends: .qa-cache + extends: + - .qa-cache + - .qa-run-e2e-with-bundler needs: [build-cng] tags: [e2e] services: @@ -29,22 +31,19 @@ workflow: GITLAB_ADMIN_USERNAME: root GITLAB_ADMIN_PASSWORD: 5iveL!fe GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN - KNAPSACK_TEST_FILE_PATTERN: "" + KNAPSACK_TEST_FILE_PATTERN: "" # overridden because currently selective test execution is skipped RSPEC_LAST_RUN_RESULTS_FILE: "$CI_PROJECT_DIR/qa/tmp/examples.txt" QA_DOCKER_NETWORK: host QA_GENERATE_ALLURE_REPORT: "true" QA_CAN_TEST_PRAEFECT: "false" QA_ALLOW_LOCAL_REQUESTS: "true" QA_SUITE_STATUS_ENV_FILE: $CI_PROJECT_DIR/suite_status.env - QA_DISABLE_RSPEC_RETRY: "true" before_script: - echo "SUITE_RAN=true" > "$QA_SUITE_STATUS_ENV_FILE" # save extra values to be available for after_script if created dynamically - echo "${EXTRA_DEPLOY_VALUES}" > $CI_PROJECT_DIR/EXTRA_DEPLOY_VALUES - export GITLAB_DOMAIN="$(getent hosts docker | awk '{ print $1 }' | head -n1).nip.io" - export QA_GITLAB_URL="http://gitlab.${GITLAB_DOMAIN}" - - source scripts/utils.sh - - source scripts/rspec_helpers.sh - cd qa && bundle install - | bundle exec cng create deployment "${DEPLOYMENT_TYPE}" \ @@ -55,15 +54,6 @@ workflow: --chart-sha "${GITLAB_HELM_CHART_REF}" \ --ci \ ${EXTRA_DEPLOY_VALUES} - script: - - export QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO:=Test::Instance::All} $QA_GITLAB_URL -- --force-color --order random --format documentation" - - echo "Running - '$QA_COMMAND'" - - | - if eval "$QA_COMMAND --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml"; then - echo "Test run finished successfully" - else - retry_failed_e2e_rspec_examples - fi after_script: - | if [ "$CI_JOB_STATUS" == "failed" ]; then diff --git a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml index c28c1dd2735..e2f12dc389a 100644 --- a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml +++ b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml @@ -48,6 +48,7 @@ include: - .qa-cache - .default-retry - .gitlab-qa-report + - .qa-run-e2e-with-bundler stage: test services: - name: docker:${DOCKER_VERSION}-dind @@ -66,26 +67,15 @@ include: QA_SUITE_STATUS_ENV_FILE: "$CI_PROJECT_DIR/suite_status.env" QA_DOCKER_NETWORK: host QA_DISABLE_RSPEC_RETRY: "true" + QA_GITLAB_URL: http://gdk.test:3000 GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN - GDK_URL: http://gdk.test:3000 FF_NETWORK_PER_BUILD: "true" RSPEC_LAST_RUN_RESULTS_FILE: "$CI_PROJECT_DIR/qa/tmp/examples.txt" before_script: - echo "SUITE_RAN=true" > "$QA_SUITE_STATUS_ENV_FILE" - echo -e "\e[0Ksection_start:`date +%s`:install_gems[collapsed=true]\r\e[0KInstall gems" - - source scripts/utils.sh - - source scripts/rspec_helpers.sh - cd qa && bundle install - echo -e "\e[0Ksection_end:`date +%s`:install_gems\r\e[0K" - script: - - export QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO:=Test::Instance::All} $GDK_URL $GITLAB_QA_OPTS -- $QA_TESTS --order random --force-color --format documentation --format QA::Support::JsonFormatter --out tmp/rspec-${CI_JOB_ID}-\${QA_RSPEC_RETRIED:-false}.json" - - echo "Running - '$QA_COMMAND'" - - | - if eval "$QA_COMMAND --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml"; then - echo "Test run finished successfully" - else - retry_failed_e2e_rspec_examples - fi after_script: - !reference [.with-gdk-log, after_script] - !reference [.gitlab-qa-report, after_script] diff --git a/app/assets/javascripts/boards/components/board_add_new_column_form.vue b/app/assets/javascripts/boards/components/board_add_new_column_form.vue index 32af92f2a0b..9d853a75f02 100644 --- a/app/assets/javascripts/boards/components/board_add_new_column_form.vue +++ b/app/assets/javascripts/boards/components/board_add_new_column_form.vue @@ -41,7 +41,7 @@ export default { diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue index 321a3819a5e..5d5ff75d3f8 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue @@ -186,7 +186,7 @@ export default { v-for="strategy in featureFlag.strategies" :key="strategy.id" data-testid="strategy-label" - class="gl-w-full gl-mr-3 gl-mt-2 gl-white-space-normal gl-text-left" + class="gl-w-full gl-mr-3 gl-mt-2 gl-whitespace-normal gl-text-left" v-bind="strategyBadgeText(strategy)" /> diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb index 59383f77474..9227ec44f5d 100644 --- a/app/controllers/admin/plan_limits_controller.rb +++ b/app/controllers/admin/plan_limits_controller.rb @@ -47,6 +47,8 @@ class Admin::PlanLimitsController < Admin::ApplicationController ci_needs_size_limit ci_registered_group_runners ci_registered_project_runners + dotenv_size + dotenv_variables pipeline_hierarchy_size ]) end diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb index c770ba696d9..d476a3c08ca 100644 --- a/app/helpers/ide_helper.rb +++ b/app/helpers/ide_helper.rb @@ -21,6 +21,18 @@ module IdeHelper ) end + def show_web_ide_oauth_callback_mismatch_callout? + return false unless ::Gitlab::WebIde::DefaultOauthApplication.feature_enabled?(current_user) + + callback_urls = ::Gitlab::WebIde::DefaultOauthApplication.oauth_application_callback_urls + callback_url_domains = callback_urls.map { |url| URI.parse(url).origin } + callback_url_domains.any? && callback_url_domains.exclude?(request.base_url) + end + + def web_ide_oauth_application_id + ::Gitlab::WebIde::DefaultOauthApplication.oauth_application_id + end + def use_new_web_ide? Feature.enabled?(:vscode_web_ide, current_user) end diff --git a/app/helpers/plan_limits_helper.rb b/app/helpers/plan_limits_helper.rb index 6a90bf8d700..10edf86f81c 100644 --- a/app/helpers/plan_limits_helper.rb +++ b/app/helpers/plan_limits_helper.rb @@ -19,6 +19,10 @@ module PlanLimitsHelper s_('AdminSettings|Maximum number of runners registered per group') when :ci_registered_project_runners s_('AdminSettings|Maximum number of runners registered per project') + when :dotenv_size + s_('AdminSettings|Maximum size of a dotenv artifact in bytes') + when :dotenv_variables + s_('AdminSettings|Maximum number of variables in a dotenv artifact') when :pipeline_hierarchy_size s_("AdminSettings|Maximum number of downstream pipelines in a pipeline's hierarchy tree") else diff --git a/app/models/integration.rb b/app/models/integration.rb index 5fb788e8d3a..1bfa6972321 100644 --- a/app/models/integration.rb +++ b/app/models/integration.rb @@ -343,6 +343,10 @@ class Integration < ApplicationRecord INSTANCE_SPECIFIC_INTEGRATION_NAMES end + def self.instance_specific_integration_types + instance_specific_integration_names.map { |name| integration_name_to_type(name) } + end + def self.dev_integration_names return [] unless Gitlab.dev_or_test_env? @@ -453,17 +457,37 @@ class Integration < ApplicationRecord end private_class_method :instance_level_integration - # Returns the number of successfully saved integrations - # Duplicate integrations are excluded from this count by their validations. - def self.create_from_active_default_integrations(owner, association) + def self.default_integrations(owner, scope) group_ids = sorted_ancestors(owner).select(:id) array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]' order = Arel.sql("type_new ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC") - - from_union([active.where(instance: true), active.where(group_id: group_ids, inherit_from_id: nil)]) + from_union([scope.where(instance: true), scope.where(group_id: group_ids, inherit_from_id: nil)]) .order(order) .group_by(&:type) - .count { |_type, parents| build_from_integration(parents.first, association => owner.id).save } + .transform_values(&:first) + end + private_class_method :default_integrations + + def self.create_from_default_integrations(owner, association) + active_default_count = create_from_active_default_integrations(owner, association) + default_instance_specific_count = create_from_default_instance_specific_integrations(owner, association) + active_default_count + default_instance_specific_count + end + + # Returns the number of successfully saved integrations + # Duplicate integrations are excluded from this count by their validations. + def self.create_from_active_default_integrations(owner, association) + default_integrations( + owner, + active.where.not(type: instance_specific_integration_types) + ).count { |_type, integration| build_from_integration(integration, association => owner.id).save } + end + + def self.create_from_default_instance_specific_integrations(owner, association) + default_integrations( + owner, + where(type: instance_specific_integration_types) + ).count { |_type, integration| build_from_integration(integration, association => owner.id).save } end def self.descendants_from_self_or_ancestors_from(integration) diff --git a/app/services/ci/update_build_names_service.rb b/app/services/ci/update_build_names_service.rb index 4bdec40a394..b3c974bd87d 100644 --- a/app/services/ci/update_build_names_service.rb +++ b/app/services/ci/update_build_names_service.rb @@ -12,7 +12,7 @@ module Ci # rubocop: disable Database/AvoidUsingPluckWithoutLimit -- plucking on batch def execute keys = %i[build_id partition_id name project_id] - pipeline.latest_builds.each_batch(of: 50) do |batch| + pipeline.builds.latest.each_batch(of: 50) do |batch| builds_upsert_data = batch .pluck(:id, :partition_id, :name, :project_id) diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index a2d3e5e15e0..723261da36b 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -67,7 +67,7 @@ module Groups if @group.save @group.add_owner(current_user) @group.add_creator(current_user) - Integration.create_from_active_default_integrations(@group, :group_id) + Integration.create_from_default_integrations(@group, :group_id) end end end diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb index 6655ce2e190..4bf14516dde 100644 --- a/app/services/groups/transfer_service.rb +++ b/app/services/groups/transfer_service.rb @@ -293,7 +293,7 @@ module Groups def update_integrations @group.integrations.with_default_settings.delete_all - Integration.create_from_active_default_integrations(@group, :group_id) + Integration.create_from_default_integrations(@group, :group_id) end def propagate_integrations diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 1b96042c1c6..e5069071d60 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -243,7 +243,7 @@ module Projects Namespaces::ProjectNamespace.create_from_project!(@project) if @project.valid? if @project.saved? - Integration.create_from_active_default_integrations(@project, :project_id) + Integration.create_from_default_integrations(@project, :project_id) @project.create_labels unless @project.gitlab_project_import? diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 84e3774ca11..86cbacced8c 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -267,7 +267,7 @@ module Projects def update_integrations project.integrations.with_default_settings.delete_all - Integration.create_from_active_default_integrations(project, :project_id) + Integration.create_from_default_integrations(project, :project_id) end def update_pending_builds diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml index a48c4fab594..850936cc4b3 100644 --- a/app/views/admin/application_settings/_ci_cd.html.haml +++ b/app/views/admin/application_settings/_ci_cd.html.haml @@ -93,6 +93,12 @@ .form-group = f.label :ci_instance_level_variables, plan_limit_setting_description(:ci_instance_level_variables) = f.number_field :ci_instance_level_variables, class: 'form-control gl-form-input' + .form-group + = f.label :dotenv_size, plan_limit_setting_description(:dotenv_size) + = f.number_field :dotenv_size, class: 'form-control gl-form-input' + .form-group + = f.label :dotenv_variables, plan_limit_setting_description(:dotenv_variables) + = f.number_field :dotenv_variables, class: 'form-control gl-form-input' .form-group = f.label :ci_pipeline_size, plan_limit_setting_description(:ci_pipeline_size) = f.number_field :ci_pipeline_size, class: 'form-control gl-form-input' diff --git a/app/views/admin/dashboard/_web_ide_oauth_callback_mismatch_callout.html.haml b/app/views/admin/dashboard/_web_ide_oauth_callback_mismatch_callout.html.haml new file mode 100644 index 00000000000..c6a1ddf8542 --- /dev/null +++ b/app/views/admin/dashboard/_web_ide_oauth_callback_mismatch_callout.html.haml @@ -0,0 +1,10 @@ +- return unless show_web_ide_oauth_callback_mismatch_callout? + += render Pajamas::AlertComponent.new(variant: :warning, + title: s_('AdminArea|Current domain doesn\'t match the Web IDE OAuth configuration'), + alert_options: { class: 'gl-mt-4' }) do |c| + - c.with_body do + = s_('AdminArea|The Web IDE OAuth application doesn\'t have a redirect URL with the domain that you are using to visit GitLab. This issue will prevent Web IDE users from authenticating and often occurs when using a reverse proxy.') + - c.with_actions do + = render Pajamas::ButtonComponent.new(variant: :confirm, href: edit_admin_application_path(web_ide_oauth_application_id), button_options: { class: 'deferred-link gl-alert-action', rel: 'noreferrer noopener' }) do + = s_('AdminArea|Edit OAuth configuration') diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 8dd2aa37da0..be6a0a8031b 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -7,6 +7,7 @@ = render_if_exists 'shared/submit_license_usage_data_banner' = render_if_exists 'shared/qrtly_reconciliation_alert' = render 'admin/dashboard/security_newsletter_callout' += render 'admin/dashboard/web_ide_oauth_callback_mismatch_callout' - if show_transition_to_jihu_callout? .js-jh-transition-banner{ data: { feature_name: Users::CalloutsHelper::TRANSITION_TO_JIHU_CALLOUT, diff --git a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml index 49b9c4c9ca6..b59b7a47922 100644 --- a/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml +++ b/app/views/clusters/clusters/cloud_providers/_cloud_provider_button.html.haml @@ -5,5 +5,5 @@ = render Pajamas::ButtonComponent.new(variant: :confirm, category: :secondary, href: help_path, button_options: { class: "gl-flex-direction-column gl-flex-basis-third" }) do %span.gl-display-flex.gl-align-items-center.gl-m-3.gl-h-64 = image_tag logo_path, alt: label, class: "gl-w-15 gl-max-h-full gl-max-w-full" - %span.gl-white-space-normal + %span.gl-whitespace-normal = label diff --git a/app/views/search/results/_milestone.html.haml b/app/views/search/results/_milestone.html.haml index 73cf3a31311..098a353efa1 100644 --- a/app/views/search/results/_milestone.html.haml +++ b/app/views/search/results/_milestone.html.haml @@ -3,7 +3,7 @@ %span.term.str-truncated= simple_search_highlight_and_truncate(milestone.title, @search_term) - if milestone.project_milestone? - .gl-mt-2= gl_badge_tag milestone.project.full_name, { variant: :muted }, { class: 'gl-white-space-normal gl-text-left' } + .gl-mt-2= gl_badge_tag milestone.project.full_name, { variant: :muted }, { class: 'gl-whitespace-normal gl-text-left' } - if milestone.description.present? .description.term diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 15645519097..fe01865f5a6 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -35,9 +35,9 @@ %div = render('shared/milestone_expired', milestone: milestone) - if milestone.group_milestone? - = gl_badge_tag milestone.group.full_name, { variant: :info }, { class: 'gl-white-space-normal gl-text-left' } + = gl_badge_tag milestone.group.full_name, { variant: :info }, { class: 'gl-whitespace-normal gl-text-left' } - if milestone.project_milestone? - = gl_badge_tag milestone.project.full_name, { variant: :muted }, { class: 'gl-white-space-normal gl-text-left' } + = gl_badge_tag milestone.project.full_name, { variant: :muted }, { class: 'gl-whitespace-normal gl-text-left' } .order-3.order-md-2.mt-2.mt-md-0.milestone-progress{ class: can_admin_milestone ? 'col-md-4' : 'col-md-5' } = milestone_progress_bar(milestone) diff --git a/config/feature_flags/beta/track_ai_metrics_in_usage_data.yml b/config/feature_flags/beta/track_ai_metrics_in_usage_data.yml new file mode 100644 index 00000000000..4141953baf2 --- /dev/null +++ b/config/feature_flags/beta/track_ai_metrics_in_usage_data.yml @@ -0,0 +1,9 @@ +--- +name: track_ai_metrics_in_usage_data +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457504 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155973 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/466664 +milestone: '17.1' +group: group::optimize +type: beta +default_enabled: false diff --git a/db/post_migrate/20240531140131_add_issues_milestone_and_id_index_concurrently.rb b/db/post_migrate/20240531140131_add_issues_milestone_and_id_index_concurrently.rb new file mode 100644 index 00000000000..f044d91cb6b --- /dev/null +++ b/db/post_migrate/20240531140131_add_issues_milestone_and_id_index_concurrently.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class AddIssuesMilestoneAndIdIndexConcurrently < Gitlab::Database::Migration[2.2] + milestone '17.1' + + INDEX_NAME = 'index_issues_on_milestone_id_and_id' + + disable_ddl_transaction! + + # rubocop: disable Migration/PreventIndexCreation -- Replacing an existing index + def up + add_concurrent_index :issues, %i[milestone_id id], name: INDEX_NAME + end + # rubocop: enable Migration/PreventIndexCreation + + def down + remove_concurrent_index_by_name :issues, INDEX_NAME + end +end diff --git a/db/post_migrate/20240531140200_remove_issues_milestone_index_concurrently.rb b/db/post_migrate/20240531140200_remove_issues_milestone_index_concurrently.rb new file mode 100644 index 00000000000..eb293f3a857 --- /dev/null +++ b/db/post_migrate/20240531140200_remove_issues_milestone_index_concurrently.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class RemoveIssuesMilestoneIndexConcurrently < Gitlab::Database::Migration[2.2] + milestone '17.1' + + disable_ddl_transaction! + + def up + remove_concurrent_index_by_name :issues, 'index_issues_on_milestone_id' + end + + def down + add_concurrent_index :issues, %i[milestone_id] + end +end diff --git a/db/post_migrate/20240607105207_remove_index_identities_on_provider.rb b/db/post_migrate/20240607105207_remove_index_identities_on_provider.rb new file mode 100644 index 00000000000..de5784a6dd1 --- /dev/null +++ b/db/post_migrate/20240607105207_remove_index_identities_on_provider.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class RemoveIndexIdentitiesOnProvider < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '17.1' + + INDEX_NAME = 'index_identities_on_provider' + + def up + remove_concurrent_index_by_name :identities, name: INDEX_NAME + end + + def down + add_concurrent_index :identities, :provider, name: INDEX_NAME + end +end diff --git a/db/schema_migrations/20240531140131 b/db/schema_migrations/20240531140131 new file mode 100644 index 00000000000..b85fe431ddd --- /dev/null +++ b/db/schema_migrations/20240531140131 @@ -0,0 +1 @@ +3914eb6a992c6df54e74f24fb5c7890faaab0c46c27506e24840054704790b56 \ No newline at end of file diff --git a/db/schema_migrations/20240531140200 b/db/schema_migrations/20240531140200 new file mode 100644 index 00000000000..60a6e0b758a --- /dev/null +++ b/db/schema_migrations/20240531140200 @@ -0,0 +1 @@ +1a01537566a911ff788e5345c806e58930e7f8d8348126df7a9490ffa4700fc3 \ No newline at end of file diff --git a/db/schema_migrations/20240607105207 b/db/schema_migrations/20240607105207 new file mode 100644 index 00000000000..5c3dd1ec42f --- /dev/null +++ b/db/schema_migrations/20240607105207 @@ -0,0 +1 @@ +f8dc0c648771a938526d6b14d9967c352fad08547a939eb177c0e6639b727e52 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 98c2e9e46a6..9135f8bf29a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -26627,8 +26627,6 @@ CREATE INDEX index_historical_data_on_recorded_at ON historical_data USING btree CREATE UNIQUE INDEX index_http_integrations_on_project_and_endpoint ON alert_management_http_integrations USING btree (project_id, endpoint_identifier); -CREATE INDEX index_identities_on_provider ON identities USING btree (provider); - CREATE INDEX index_identities_on_saml_provider_id ON identities USING btree (saml_provider_id) WHERE (saml_provider_id IS NOT NULL); CREATE INDEX index_identities_on_user_id ON identities USING btree (user_id); @@ -26791,7 +26789,7 @@ CREATE INDEX index_issues_on_id_and_weight ON issues USING btree (id, weight); CREATE INDEX index_issues_on_last_edited_by_id ON issues USING btree (last_edited_by_id); -CREATE INDEX index_issues_on_milestone_id ON issues USING btree (milestone_id); +CREATE INDEX index_issues_on_milestone_id_and_id ON issues USING btree (milestone_id, id); CREATE INDEX index_issues_on_moved_to_id ON issues USING btree (moved_to_id) WHERE (moved_to_id IS NOT NULL); diff --git a/doc/administration/settings/account_and_limit_settings.md b/doc/administration/settings/account_and_limit_settings.md index 782fc7a3a89..b0d1b2600fb 100644 --- a/doc/administration/settings/account_and_limit_settings.md +++ b/doc/administration/settings/account_and_limit_settings.md @@ -339,6 +339,37 @@ GitLab administrators can prevent this behavior: 1. Clear the **Allow users with up to Guest role to create groups and personal projects** checkbox. 1. Select **Save changes**. +## Allow users to make their profiles private + +DETAILS: +**Tier:** Premium, Ultimate +**Offering:** Self-managed +**Status:** Experiment + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421310) in GitLab 17.1 [with a flag](../../administration/feature_flags.md) named `disallow_private_profiles`. Disabled by default. + +FLAG: +The availability of this feature is controlled by a feature flag. +For more information, see the history. +This feature is available for testing, but not ready for production use. + +By default, users can make their profiles private. +GitLab administrators can disable this setting to prevent users from making their profiles private: + +1. On the left sidebar, at the bottom, select **Admin Area**. +1. Select **Settings > General**. +1. Expand **Account and limit**. +1. Clear the **Allow users to make their profiles private** checkbox. +1. Select **Save changes**. + +NOTE: +If this setting is disabled, [Set profiles of new users to private by default](#set-profiles-of-new-users-to-private-by-default) is also disabled. + +WARNING: +When this setting is disabled, it doesn't mark existing private profiles as public. +GitLab administrators must manually update all existing private profiles back to public. +For more information, see [issue 461701](https://gitlab.com/gitlab-org/gitlab/-/issues/461701). + ## Set profiles of new users to private by default > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/231301) in GitLab 15.8. @@ -351,6 +382,9 @@ By default, newly created users have a public profile. GitLab administrators can 1. Select the **Make new users' profiles private by default** checkbox. 1. Select **Save changes**. +NOTE: +If [Allow users to make their profiles private](#allow-users-to-make-their-profiles-private) is disabled, this setting is also disabled. + ## Prevent users from deleting their accounts DETAILS: diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index a17842b1f52..c70b1941122 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -13407,6 +13407,54 @@ The edge type for [`MergeRequestReviewer`](#mergerequestreviewer). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`MergeRequestReviewer`](#mergerequestreviewer) | The item at the end of the edge. | +#### `MergeTrainCarConnection` + +The connection type for [`MergeTrainCar`](#mergetraincar). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `count` | [`Int!`](#int) | Total count of collection. | +| `edges` | [`[MergeTrainCarEdge]`](#mergetraincaredge) | A list of edges. | +| `nodes` | [`[MergeTrainCar]`](#mergetraincar) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `MergeTrainCarEdge` + +The edge type for [`MergeTrainCar`](#mergetraincar). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`MergeTrainCar`](#mergetraincar) | The item at the end of the edge. | + +#### `MergeTrainConnection` + +The connection type for [`MergeTrain`](#mergetrain). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `count` | [`Int!`](#int) | Total count of collection. | +| `edges` | [`[MergeTrainEdge]`](#mergetrainedge) | A list of edges. | +| `nodes` | [`[MergeTrain]`](#mergetrain) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `MergeTrainEdge` + +The edge type for [`MergeTrain`](#mergetrain). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`MergeTrain`](#mergetrain) | The item at the end of the edge. | + #### `MilestoneConnection` The connection type for [`Milestone`](#milestone). @@ -25675,6 +25723,58 @@ four standard [pagination arguments](#pagination-arguments): | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in GitLab 16.7. Use actual_states instead. | | `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | +### `MergeTrain` + +Represents a set of cars/merge_requests queued for merging. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `targetBranch` | [`String!`](#string) | Target branch of the car's merge request. | + +#### Fields with arguments + +##### `MergeTrain.cars` + +Cars queued in the train. + +DETAILS: +**Introduced** in GitLab 17.1. +**Status**: Experiment. + +Returns [`MergeTrainCarConnection!`](#mergetraincarconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#pagination-arguments): +`before: String`, `after: String`, `first: Int`, and `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `activityStatus` | [`MergeTrainStatus!`](#mergetrainstatus) | Filter cars by their high-level status. Defaults to ACTIVE. | + +### `MergeTrainCar` + +MergeTrainCar represents an attempt to merge a merge requestusing merge trains. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `createdAt` | [`Time!`](#time) | Timestamp of when the car was created. | +| `duration` | [`Int`](#int) | Duration of the car. | +| `id` | [`MergeTrainsCarID!`](#mergetrainscarid) | Global ID of the car. | +| `mergeRequest` | [`MergeRequest!`](#mergerequest) | Merge request the car contains. | +| `mergedAt` | [`Time`](#time) | Timestamp of when the car was merged. | +| `pipeline` | [`Pipeline`](#pipeline) | Pipeline of the car. | +| `status` | [`CarStatus!`](#carstatus) | Status of the car. | +| `targetBranch` | [`Branch!`](#branch) | Target branch of the car's merge request. | +| `targetProject` | [`Project!`](#project) | Project the car's MR targets. | +| `updatedAt` | [`Time!`](#time) | Timestamp of when the car was last updated. | +| `user` | [`UserCore!`](#usercore) | Creator of the car (user who added the merge request to the train). | + ### `Metadata` #### Fields @@ -28439,6 +28539,27 @@ four standard [pagination arguments](#pagination-arguments): | `updatedAfter` | [`Time`](#time) | Merge requests updated after the timestamp. | | `updatedBefore` | [`Time`](#time) | Merge requests updated before the timestamp. | +##### `Project.mergeTrains` + +Merge trains available to the project. + +DETAILS: +**Introduced** in GitLab 17.1. +**Status**: Experiment. + +Returns [`MergeTrainConnection`](#mergetrainconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#pagination-arguments): +`before: String`, `after: String`, `first: Int`, and `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `status` | [`MergeTrainStatus`](#mergetrainstatus) | Filter merge trains by a specific status. Defaults to ACTIVE. | +| `targetBranches` | [`[String!]`](#string) | Filter merge trains by a list of target branches. | + ##### `Project.milestones` Milestones of the project. @@ -33114,6 +33235,19 @@ Types of blob viewers. | `rich` | Rich blob viewers type. | | `simple` | Simple blob viewers type. | +### `CarStatus` + +Status of a merge train's car. + +| Value | Description | +| ----- | ----------- | +| `FRESH` | Car's status: fresh. | +| `IDLE` | Car's status: idle. | +| `MERGED` | Car's status: merged. | +| `MERGING` | Car's status: merging. | +| `SKIP_MERGED` | Car's status: skip_merged. | +| `STALE` | Car's status: stale. | + ### `CiCatalogResourceScope` Values for scoping catalog resources. @@ -34613,6 +34747,13 @@ Representation of whether a GitLab merge request can be merged. | `MERGE_WHEN_CHECKS_PASS` | Use the merge_when_checks_pass merge strategy. | | `MERGE_WHEN_PIPELINE_SUCCEEDS` | Use the merge_when_pipeline_succeeds merge strategy. | +### `MergeTrainStatus` + +| Value | Description | +| ----- | ----------- | +| `ACTIVE` | Active merge train. | +| `COMPLETED` | Completed merge train. | + ### `MergeabilityCheckIdentifier` Representation of mergeability check identifier. @@ -36598,6 +36739,12 @@ A `MergeRequestsExternalStatusCheckID` is a global ID. It is encoded as a string An example `MergeRequestsExternalStatusCheckID` is: `"gid://gitlab/MergeRequests::ExternalStatusCheck/1"`. +### `MergeTrainsCarID` + +A `MergeTrainsCarID` is a global ID. It is encoded as a string. + +An example `MergeTrainsCarID` is: `"gid://gitlab/MergeTrains::Car/1"`. + ### `MilestoneID` A `MilestoneID` is a global ID. It is encoded as a string. diff --git a/doc/api/plan_limits.md b/doc/api/plan_limits.md index 32b75b7724d..3e5f58ce331 100644 --- a/doc/api/plan_limits.md +++ b/doc/api/plan_limits.md @@ -45,6 +45,8 @@ Example response: "ci_needs_size_limit": 50, "ci_registered_group_runners": 1000, "ci_registered_project_runners": 1000, + "dotenv_size": 5120, + "dotenv_variables": 20, "conan_max_file_size": 3221225472, "enforcement_limit": 10000, "generic_packages_max_file_size": 5368709120, @@ -78,6 +80,8 @@ PUT /application/plan_limits | `ci_needs_size_limit` | integer | no | Maximum number of [DAG](../ci/directed_acyclic_graph/index.md) dependencies that a job can have. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. | | `ci_registered_group_runners` | integer | no | Maximum number of runners registered per group. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. | | `ci_registered_project_runners` | integer | no | Maximum number of runners registered per project. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85895) in GitLab 15.0. | +| `dotenv_size` | integer | no | Maximum size of a dotenv artifact in bytes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/432529) in GitLab 17.1. | +| `dotenv_variables` | integer | no | Maximum number of variables in a dotenv artifact. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/432529) in GitLab 17.1. | | `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. | | `enforcement_limit` | integer | no | Maximum storage size for root namespace limit enforcement in MiB. | | `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. | @@ -107,6 +111,8 @@ Example response: "ci_registered_group_runners": 1000, "ci_registered_project_runners": 1000, "conan_max_file_size": 3221225472, + "dotenv_variables": 20, + "dotenv_size": 5120, "generic_packages_max_file_size": 5368709120, "helm_max_file_size": 5242880, "maven_max_file_size": 3221225472, diff --git a/doc/user/ai_experiments.md b/doc/user/ai_experiments.md index 01c2a0d2d72..2f99017fbc1 100644 --- a/doc/user/ai_experiments.md +++ b/doc/user/ai_experiments.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo/experiments.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo/experiments.md). - + diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md index 2ce50c033d7..0d74ab7d357 100644 --- a/doc/user/ai_features.md +++ b/doc/user/ai_features.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo/index.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo/index.md). - + diff --git a/doc/user/ai_features_enable.md b/doc/user/ai_features_enable.md index 8f29c3581f5..36e1edf7caa 100644 --- a/doc/user/ai_features_enable.md +++ b/doc/user/ai_features_enable.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo/turn_on_off.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo/turn_on_off.md). - + diff --git a/doc/user/application_security/secret_detection/pipeline/detected_secrets.md b/doc/user/application_security/secret_detection/pipeline/detected_secrets.md index 96db687a07c..4d5545a0e65 100644 --- a/doc/user/application_security/secret_detection/pipeline/detected_secrets.md +++ b/doc/user/application_security/secret_detection/pipeline/detected_secrets.md @@ -6,6 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Detected secrets +DETAILS: +**Tier:** Free, Premium, Ultimate +**Offering:** GitLab.com, Self-managed, GitLab Dedicated +**Status:** GA + This table lists the secrets detected by [pipeline secret detection](index.md). diff --git a/doc/user/application_security/secret_detection/secret_push_protection/detected_secrets.md b/doc/user/application_security/secret_detection/secret_push_protection/detected_secrets.md index 4f69dd56eb3..39559ead11a 100644 --- a/doc/user/application_security/secret_detection/secret_push_protection/detected_secrets.md +++ b/doc/user/application_security/secret_detection/secret_push_protection/detected_secrets.md @@ -6,6 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Detected secrets +DETAILS: +**Tier:** Ultimate +**Offering:** GitLab.com, GitLab Dedicated +**Status:** Beta + This table lists the secrets detected by [secret push protection](index.md). diff --git a/doc/user/get_started/get_started_managing_code.md b/doc/user/get_started/get_started_managing_code.md index b4133c92f86..8b80770a75b 100644 --- a/doc/user/get_started/get_started_managing_code.md +++ b/doc/user/get_started/get_started_managing_code.md @@ -104,7 +104,7 @@ For details, see: - [Request a review of your merge request](../project/merge_requests/reviews/index.md#request-a-review) - [Add suggestions to a merge request](../project/merge_requests/reviews/suggestions.md#create-suggestions) -- [How approvals work](../project/merge_requests/approvals/index.md#how-approvals-work) +- [Merge request approvals](../project/merge_requests/approvals/index.md) ## Step 5: Merge the merge request diff --git a/doc/user/gitlab_duo/experiments.md b/doc/user/gitlab_duo/experiments.md index f9862fa0e16..e90d312b8c4 100644 --- a/doc/user/gitlab_duo/experiments.md +++ b/doc/user/gitlab_duo/experiments.md @@ -19,15 +19,14 @@ DETAILS: > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10344) in GitLab 16.0 as an [experiment](../../policy/experiment-beta-support.md#experiment). -To use this feature: +Generate a summary of discussions on an issue. -- The parent group of the issue must: - - Enable the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features). -- You must: - - Belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. - - Have sufficient permissions to view the issue. +Prerequisites: -You can generate a summary of discussions on an issue: +- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. +- You must have permission to view the issue. + +To generate a summary of issue discussions: 1. In an issue, scroll to the **Activity** section. 1. Select **View summary**. @@ -49,15 +48,15 @@ DETAILS: > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10228) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment). -To use this feature: +Improve your planning and decision-making by predicting productivity metrics and +identifying anomalies across your software development lifecycle. -- The parent group of the project must: - - Enable the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features). -- You must: - - Belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. - - Have sufficient permissions to view the CI/CD analytics. +Prerequisites: -In CI/CD Analytics, you can view a forecast of deployment frequency: +- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. +- You must have permission to view the CI/CD analytics. + +To view a forecast of deployment frequency in CI/CD Analytics: 1. On the left sidebar, select **Search or go to** and find your project. 1. Select **Analyze > CI/CD analytics**. @@ -65,14 +64,17 @@ In CI/CD Analytics, you can view a forecast of deployment frequency: 1. Turn on the **Show forecast** toggle. 1. On the confirmation dialog, select **Accept testing terms**. -The forecast is displayed as a dotted line on the chart. Data is forecasted for a duration that is half of the selected date range. -For example, if you select a 30-day range, a forecast for the following 15 days is displayed. +The forecast is displayed as a dotted line on the chart. Data is forecasted for +a duration that is half of the selected date range. + +For example, if you select a 30-day range, a forecast for the following 15 days +is displayed. ![Forecast deployment frequency](img/forecast_deployment_frequency.png) Provide feedback on this experimental feature in [issue 416833](https://gitlab.com/gitlab-org/gitlab/-/issues/416833). -## Root cause analysis +## Troubleshoot failed CI/CD jobs with Root cause analysis DETAILS: **Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md). @@ -81,15 +83,12 @@ DETAILS: > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment). -You can learn more about failed CI/CD jobs by using GitLab Duo Root cause analysis. +Determine the root cause of a CI/CD job failure by analyzing the logs. Prerequisites: -- The parent group of the project must: - - Enable the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features). -- You must: - - Belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. - - Have sufficient permissions to view the CI/CD job. +- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. +- You must have permission to view the CI/CD job. To view root cause analysis: @@ -108,15 +107,14 @@ DETAILS: > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/10762) in GitLab 16.3 as an [experiment](../../policy/experiment-beta-support.md#experiment). -To use this feature: +Generate a detailed description for an issue based on a short summary you provide. -- The parent group of the project must: - - Enable the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features). -- You must: - - Belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. - - Have sufficient permissions to view the issue. +Prerequisites: -You can generate the description for an issue from a short summary. +- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled. +- You must have permission to view the issue. + +To generate an issue description: 1. Create a new issue. 1. Above the **Description** field, select **GitLab Duo** (**{tanuki-ai}**) **> Generate issue description**. diff --git a/doc/user/gitlab_duo/index.md b/doc/user/gitlab_duo/index.md index 7dce205562a..b6c4468ef37 100644 --- a/doc/user/gitlab_duo/index.md +++ b/doc/user/gitlab_duo/index.md @@ -198,7 +198,7 @@ DETAILS: - Helps you determine the root cause for a CI/CD job failure by analyzing the logs. - LLM: Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text) -- [View documentation](experiments.md#root-cause-analysis). +- [View documentation](experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). ### Vulnerability resolution diff --git a/doc/user/gitlab_duo/use_cases.md b/doc/user/gitlab_duo/use_cases.md index beed7a5de72..4c578df4d17 100644 --- a/doc/user/gitlab_duo/use_cases.md +++ b/doc/user/gitlab_duo/use_cases.md @@ -51,7 +51,7 @@ CI/CD configuration is needed. Please show a .gitignore and .gitlab-ci.yml configuration for a C# project. ``` -- If your CI/CD job fails, [Root Cause Analysis](../../user/gitlab_duo/experiments.md#root-cause-analysis) +- If your CI/CD job fails, [Root Cause Analysis](../../user/gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) can help understand the problem. Alternatively, you can copy the error message into GitLab Duo Chat, and ask for help: diff --git a/doc/user/gitlab_duo_chat.md b/doc/user/gitlab_duo_chat.md index 2adfe77e780..45276702a10 100644 --- a/doc/user/gitlab_duo_chat.md +++ b/doc/user/gitlab_duo_chat.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo_chat/index.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo_chat/index.md). - + diff --git a/doc/user/gitlab_duo_chat/examples.md b/doc/user/gitlab_duo_chat/examples.md index 8eb2ad22067..0822fa17b8f 100644 --- a/doc/user/gitlab_duo_chat/examples.md +++ b/doc/user/gitlab_duo_chat/examples.md @@ -78,7 +78,7 @@ You can also ask to explain specific job errors by copy-pasting the error messag - `Please explain this CI/CD job error message in the context of a Go project: build.sh: line 14: go command not found` -Alternatively, you can use [root cause analysis in CI/CD](../gitlab_duo/experiments.md#root-cause-analysis). +Alternatively, you can use [root cause analysis in CI/CD](../gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis). ## Explain code in the IDE diff --git a/doc/user/gitlab_duo_chat/index.md b/doc/user/gitlab_duo_chat/index.md index cc4c5679a00..6cea63ba91e 100644 --- a/doc/user/gitlab_duo_chat/index.md +++ b/doc/user/gitlab_duo_chat/index.md @@ -51,7 +51,7 @@ You can express interest in other IDE extension support 1. Optional. Ask a follow-up question. To ask a new question unrelated to the previous conversation, you might receive better answers -if you clear the context by typing `/reset` and selecting **Send**. +if you clear the context by typing `/reset` or `/clear` and selecting **Send**. NOTE: Only the last 50 messages are retained in the chat history. The chat history expires 3 days after last use. @@ -152,12 +152,19 @@ Alternatively, use the context menu to perform these tasks. When you use a slash command, you can also add additional instructions, for example: `/tests using the Boost.Test framework`. -## Delete all conversations +## Delete or reset the conversation -To delete all previous conversations: +To delete all conversations permanently and clear the chat window: - In the text box, type `/clear` and select **Send**. +To start a new conversation, but keep the previous conversations visible in the chat window: + +- In the text box, type `/reset` and select **Send**. + +In both cases, the conversation history will not be considered when you ask new questions. +Deleting or resetting might help improve the answers when you switch contexts, because Duo Chat will not get confused by the unrelated conversations. + ## Give feedback Your feedback is important to us as we continually enhance your GitLab Duo Chat experience. diff --git a/doc/user/gitlab_duo_chat_enable.md b/doc/user/gitlab_duo_chat_enable.md index 000ca73480a..d1159b34dcc 100644 --- a/doc/user/gitlab_duo_chat_enable.md +++ b/doc/user/gitlab_duo_chat_enable.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo_chat/turn_on_off.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo_chat/turn_on_off.md). - + diff --git a/doc/user/gitlab_duo_chat_examples.md b/doc/user/gitlab_duo_chat_examples.md index f0a6e054abe..f1e8d08cea6 100644 --- a/doc/user/gitlab_duo_chat_examples.md +++ b/doc/user/gitlab_duo_chat_examples.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo_chat/examples.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo_chat/examples.md). - + diff --git a/doc/user/gitlab_duo_chat_troubleshooting.md b/doc/user/gitlab_duo_chat_troubleshooting.md index 2d704475ced..bb33f650879 100644 --- a/doc/user/gitlab_duo_chat_troubleshooting.md +++ b/doc/user/gitlab_duo_chat_troubleshooting.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo_chat/troubleshooting.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo_chat/troubleshooting.md). - + diff --git a/doc/user/gitlab_duo_examples.md b/doc/user/gitlab_duo_examples.md index ffcd541f9b9..391e202536a 100644 --- a/doc/user/gitlab_duo_examples.md +++ b/doc/user/gitlab_duo_examples.md @@ -1,11 +1,11 @@ --- redirect_to: '../user/gitlab_duo/use_cases.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](../user/gitlab_duo/use_cases.md). - + diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 9751d727800..24fa472c9ae 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -103,6 +103,9 @@ You can also [use the API to delete a secondary email address](../../api/users.m You can make your user profile visible to only you and GitLab administrators. +NOTE: +A GitLab administrator can [disable](../../administration/settings/account_and_limit_settings.md#allow-users-to-make-their-profiles-private) this setting, forcing all profiles to be made public. + To make your profile private: 1. On the left sidebar, select your avatar. diff --git a/doc/user/project/merge_requests/ai_in_merge_requests.md b/doc/user/project/merge_requests/ai_in_merge_requests.md index 90eb8770d74..f92526f6c0a 100644 --- a/doc/user/project/merge_requests/ai_in_merge_requests.md +++ b/doc/user/project/merge_requests/ai_in_merge_requests.md @@ -1,11 +1,11 @@ --- redirect_to: 'duo_in_merge_requests.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](duo_in_merge_requests.md). - + diff --git a/doc/user/project/merge_requests/approvals/img/approvals_satisfied_v17_1.png b/doc/user/project/merge_requests/approvals/img/approvals_satisfied_v17_1.png new file mode 100644 index 00000000000..fc1fe245305 Binary files /dev/null and b/doc/user/project/merge_requests/approvals/img/approvals_satisfied_v17_1.png differ diff --git a/doc/user/project/merge_requests/approvals/img/approvals_unsatisfied_v17_1.png b/doc/user/project/merge_requests/approvals/img/approvals_unsatisfied_v17_1.png new file mode 100644 index 00000000000..f1b8737421c Binary files /dev/null and b/doc/user/project/merge_requests/approvals/img/approvals_unsatisfied_v17_1.png differ diff --git a/doc/user/project/merge_requests/approvals/img/you_approvals_satisfied_v17_1.png b/doc/user/project/merge_requests/approvals/img/you_approvals_satisfied_v17_1.png new file mode 100644 index 00000000000..8d2fc370e1a Binary files /dev/null and b/doc/user/project/merge_requests/approvals/img/you_approvals_satisfied_v17_1.png differ diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md index 00de1fc9d04..ddfe47599f0 100644 --- a/doc/user/project/merge_requests/approvals/index.md +++ b/doc/user/project/merge_requests/approvals/index.md @@ -11,28 +11,46 @@ DETAILS: **Tier:** Free, Premium, Ultimate **Offering:** GitLab.com, Self-managed, GitLab Dedicated -You can configure your merge requests so that they must be approved before -they can be merged. While [GitLab Free](https://about.gitlab.com/pricing/) allows +You can configure your merge requests to allow (or require) approval before +they merge. While [GitLab Free](https://about.gitlab.com/pricing/) allows all users with Developer or greater [permissions](../../../permissions.md) to approve merge requests, these approvals are [optional](#optional-approvals). [GitLab Premium](https://about.gitlab.com/pricing/) and -[GitLab Ultimate](https://about.gitlab.com/pricing/) provide additional +[GitLab Ultimate](https://about.gitlab.com/pricing/) give you more flexibility: - Create required [rules](rules.md) about the number and type of approvers before work can merge. -- Specify a list of users who act as [code owners](../../codeowners/index.md) for specific files, +- Build a list of users who act as [code owners](../../codeowners/index.md) for specific files, and require their approval before work can merge. - For GitLab Premium and GitLab Ultimate, configure approvals [for the entire instance](../../../../administration/merge_requests_approvals.md). -You can configure merge request approvals on a per-project basis, and some approvals can be configured -[on the group level](../../../group/manage.md#group-merge-request-approval-settings). Support for -group-level settings for merge request approval rules is tracked in this -[epic](https://gitlab.com/groups/gitlab-org/-/epics/4367). +You can configure merge request approvals on a per-project basis, and configure some approvals +[at the group level](../../../group/manage.md#group-merge-request-approval-settings). Support for +group-level settings for merge request approval rules is tracked in +[epic 4367](https://gitlab.com/groups/gitlab-org/-/epics/4367). -## How approvals work +## View approval status -With [merge request approval rules](rules.md), you can set the minimum number of +The list of merge requests for your project shows the approval status for each merge request: + +| Example | Description | +| :-----: | :---------- | +| ![Approvals not yet satisfied](img/approvals_unsatisfied_v17_1.png) | Required approvals are missing. (**{approval}**) | +| ![Approvals are satisfied](img/approvals_satisfied_v17_1.png) | Approvals are satisfied. (**{check}**) | +| ![Approvals are satisfied, and you approved](img/you_approvals_satisfied_v17_1.png) | Approvals are satisfied, and you are one of the approvers. (**{approval-solid}**) | + +When an [eligible approver](rules.md#eligible-approvers) visits an open merge request, +GitLab shows one of these statuses after the body of the merge request: + +- **Approve**: The merge request doesn't yet have the required number of approvals. +- **Approve additionally**: The merge request has the required number of approvals. +- **Revoke approval**: The user viewing the merge request has already approved + the merge request. + +## Configuration options for approvals + +Use [merge request approval rules](rules.md) to set the minimum number of required approvals before work can merge into your project. You can also extend these rules to define what types of users can approve work. Some examples of rules you can create include: @@ -51,44 +69,15 @@ You can also configure: - Merge request approval rules and settings through the GitLab UI or with the [Merge request approvals API](../../../../api/merge_request_approvals.md). -Approvals cannot be changed after a merge request is merged. +You can't change the approvals on a merge request after it merges. -## Approve a merge request - -When an [eligible approver](rules.md#eligible-approvers) visits an open merge request, -GitLab displays one of these buttons after the body of the merge request: - -- **Approve**: The merge request doesn't yet have the required number of approvals. -- **Approve additionally**: The merge request has the required number of approvals. -- **Revoke approval**: The user viewing the merge request has already approved - the merge request. - -Eligible approvers can also use the `/approve` -[quick action](../../../project/quick_actions.md) when adding a comment to -a merge request. Users in the reviewer list who have approved a merge request display -a green check mark (**{check-circle-filled}**) next to their name. - -After a merge request receives the [number and type of approvals](rules.md) you configure, it can merge -unless it's blocked for another reason. Merge requests can be blocked by other problems, -such as merge conflicts, [unresolved threads](../index.md#prevent-merge-unless-all-threads-are-resolved), -or a [failed CI/CD pipeline](../merge_when_pipeline_succeeds.md). - -To prevent merge request authors from approving their own merge requests, -enable [**Prevent author approval**](settings.md#prevent-approval-by-author) -in your project's settings. - -If you enable [approval rule overrides](settings.md#prevent-editing-approval-rules-in-merge-requests), -merge requests created before a change to default approval rules are not affected. -The only exceptions are changes to the [target branch](rules.md#approvals-for-protected-branches) -of the rule. - -## Optional approvals +### Optional approvals GitLab allows all users with Developer or greater [permissions](../../../permissions.md) to approve merge requests. Approvals in GitLab Free are optional, and don't prevent a merge request from merging without approval. -## Required approvals +### Required approvals DETAILS: **Tier:** Premium, Ultimate @@ -110,36 +99,49 @@ Without the approvals, the work cannot merge. Required approvals enable multiple - Users on GitLab Ultimate can also [require approval from a security team](../../../application_security/index.md#security-approvals-in-merge-requests) before merging code that could introduce a vulnerability. +## Approve a merge request + +Eligible approvers can also use the `/approve` +[quick action](../../../project/quick_actions.md) when adding a comment to +a merge request. Users in the reviewer list who have approved a merge request display +a green check mark (**{check-circle-filled}**) next to their name. + +After a merge request receives the [number and type of approvals](rules.md) you configure, it can merge +unless it's blocked for another reason. Merge requests can be blocked by other problems, +such as merge conflicts, [unresolved threads](../index.md#prevent-merge-unless-all-threads-are-resolved), +or a [failed CI/CD pipeline](../merge_when_pipeline_succeeds.md). + +To prevent merge request authors from approving their own merge requests, +enable [**Prevent author approval**](settings.md#prevent-approval-by-author) +in your project's settings. + +If you enable [approval rule overrides](settings.md#prevent-editing-approval-rules-in-merge-requests), +merge requests created before a change to default approval rules are not affected. +The only exceptions are changes to the [target branch](rules.md#approvals-for-protected-branches) +of the rule. + ## Invalid rules > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334698) in GitLab 15.1. > - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/389905) in GitLab 15.11 [with a flag](../../../../administration/feature_flags.md) named `invalid_scan_result_policy_prevents_merge`. Disabled by default. > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/405023) in GitLab 16.2. Feature flag `invalid_scan_result_policy_prevents_merge` removed. -Whenever an approval rule cannot be satisfied, the rule is displayed as **Auto approved**. This applies to the following conditions: +When an approval rule is impossible to satisfy, GitLab shows the rule as +**Auto approved**. This happens when: -- The only eligible approver is the author of the merge request. -- No eligible approvers (either groups or users) have been assigned to the approval rule. -- The number of required approvals is more than the number of eligible approvers. +- The only eligible approver is also the merge request author. +- No eligible approvers (either groups or users) are assigned to the approval rule. +- The number of required approvals is greater than the number of eligible approvers. -These rules are automatically approved to unblock their respective merge requests, unless they were -created through a [merge request approval policy](../../../application_security/policies/scan-result-policies.md). -Invalid approval rules created through a merge request approval policy are presented with -**Action required** and are not automatically approved, blocking their respective merge requests. +These rules are automatically approved to unblock their respective merge requests, unless you +created them through a [merge request approval policy](../../../application_security/policies/scan-result-policies.md). +Invalid approval rules created through a merge request approval policy are: + +- Shown with **Action required**. +- Not automatically approved. +- Blockers for merge requests they affect. ## Related topics - [Merge request approvals API](../../../../api/merge_request_approvals.md) - [Instance-level approval rules](../../../../administration/merge_requests_approvals.md) for self-managed installations - - diff --git a/doc/user/project/repository/file_finder.md b/doc/user/project/repository/file_finder.md index d6987e417c8..a13c1113998 100644 --- a/doc/user/project/repository/file_finder.md +++ b/doc/user/project/repository/file_finder.md @@ -1,11 +1,11 @@ --- redirect_to: 'files/index.md' -remove_date: '2024-09-07' +remove_date: '2025-06-11' --- This document was moved to [another location](files/index.md). - + diff --git a/lib/api/admin/plan_limits.rb b/lib/api/admin/plan_limits.rb index 3879321516a..064c989d601 100644 --- a/lib/api/admin/plan_limits.rb +++ b/lib/api/admin/plan_limits.rb @@ -62,6 +62,8 @@ module API optional :ci_registered_group_runners, type: Integer, desc: 'Maximum number of runners registered per group' optional :ci_registered_project_runners, type: Integer, desc: 'Maximum number of runners registered per project' optional :conan_max_file_size, type: Integer, desc: 'Maximum Conan package file size in bytes' + optional :dotenv_size, type: Integer, desc: 'Maximum size of a dotenv artifact in bytes' + optional :dotenv_variables, type: Integer, desc: 'Maximum number of variables in a dotenv artifact' optional :enforcement_limit, type: Integer, desc: 'Maximum storage size for the root namespace enforcement in MiB' optional :generic_packages_max_file_size, type: Integer, desc: 'Maximum generic package file size in bytes' diff --git a/lib/api/entities/plan_limit.rb b/lib/api/entities/plan_limit.rb index 8bb7a37f9b5..df6ec482839 100644 --- a/lib/api/entities/plan_limit.rb +++ b/lib/api/entities/plan_limit.rb @@ -12,6 +12,8 @@ module API expose :ci_registered_group_runners, documentation: { type: 'integer', example: 1000 } expose :ci_registered_project_runners, documentation: { type: 'integer', example: 1000 } expose :conan_max_file_size, documentation: { type: 'integer', example: 3221225472 } + expose :dotenv_variables, documentation: { type: 'integer', example: 20 } + expose :dotenv_size, documentation: { type: 'integer', example: 5120 } expose :enforcement_limit, documentation: { type: 'integer', example: 15000 } expose :generic_packages_max_file_size, documentation: { type: 'integer', example: 5368709120 } expose :helm_max_file_size, documentation: { type: 'integer', example: 5242880 } diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb index 8f174806eed..a96969cb1d8 100644 --- a/lib/api/usage_data.rb +++ b/lib/api/usage_data.rb @@ -108,7 +108,9 @@ module API .fetch(:additional_properties, Gitlab::InternalEvents::DEFAULT_ADDITIONAL_PROPERTIES) .symbolize_keys - unless Gitlab::Tracking::AiTracking.track_via_code_suggestions?(event_name, current_user) + if Feature.enabled?(:track_ai_metrics_in_usage_data) && # rubocop:disable Gitlab/FeatureFlagWithoutActor -- beta + !Gitlab::Tracking::AiTracking.track_via_code_suggestions?(event_name, current_user) + Gitlab::Tracking::AiTracking.track_event(event_name, additional_properties.merge(user: current_user)) end diff --git a/lib/banzai/filter/wiki_link_gollum_filter.rb b/lib/banzai/filter/wiki_link_gollum_filter.rb index 97c5ee1f34c..df29269d7e7 100644 --- a/lib/banzai/filter/wiki_link_gollum_filter.rb +++ b/lib/banzai/filter/wiki_link_gollum_filter.rb @@ -30,7 +30,7 @@ module Banzai # Pattern to match allowed image extensions ALLOWED_IMAGE_EXTENSIONS = /(jpg|png|gif|svg|bmp)\z/i - CSS_WIKILINK_STYLE = 'a[data-wikilink]' + CSS_WIKILINK_STYLE = 'a[href][data-wikilink]' XPATH_WIKILINK_STYLE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_WIKILINK_STYLE).freeze def call diff --git a/lib/gitlab/web_ide/default_oauth_application.rb b/lib/gitlab/web_ide/default_oauth_application.rb index 1e609456f0d..6b0ce12ca37 100644 --- a/lib/gitlab/web_ide/default_oauth_application.rb +++ b/lib/gitlab/web_ide/default_oauth_application.rb @@ -16,7 +16,13 @@ module Gitlab Gitlab::Routing.url_helpers.ide_oauth_redirect_url end + def oauth_application_id + oauth_application ? oauth_application.id : nil + end + def oauth_application_callback_urls + return [] unless oauth_application + URI.extract(oauth_application.redirect_uri, %w[http https]).uniq end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 346a6bc6a5e..9b31fd23aa7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3485,12 +3485,18 @@ msgstr "" msgid "AdminArea|Components" msgstr "" +msgid "AdminArea|Current domain doesn't match the Web IDE OAuth configuration" +msgstr "" + msgid "AdminArea|Developer" msgstr "" msgid "AdminArea|Documentation" msgstr "" +msgid "AdminArea|Edit OAuth configuration" +msgstr "" + msgid "AdminArea|Features" msgstr "" @@ -3557,6 +3563,9 @@ msgstr "" msgid "AdminArea|Sign up for the GitLab newsletter" msgstr "" +msgid "AdminArea|The Web IDE OAuth application doesn't have a redirect URL with the domain that you are using to visit GitLab. This issue will prevent Web IDE users from authenticating and often occurs when using a reverse proxy." +msgstr "" + msgid "AdminArea|Total Billable users" msgstr "" @@ -3908,6 +3917,12 @@ msgstr "" msgid "AdminSettings|Maximum number of runners registered per project" msgstr "" +msgid "AdminSettings|Maximum number of variables in a dotenv artifact" +msgstr "" + +msgid "AdminSettings|Maximum size of a dotenv artifact in bytes" +msgstr "" + msgid "AdminSettings|Minimum size must be at least 0." msgstr "" diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh index b9bc9765276..b025c2a7f53 100644 --- a/scripts/rspec_helpers.sh +++ b/scripts/rspec_helpers.sh @@ -292,6 +292,21 @@ function rspec_parallelized_job() { handle_retry_rspec_in_new_process $rspec_run_status } +function run_e2e_specs() { + local url=$1 + local tests=$2 + local tags=$3 + + export QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO:=Test::Instance::All} $url -- $tests $tags --order random --force-color --format documentation --format QA::Support::JsonFormatter --out tmp/rspec-${CI_JOB_ID}-\${QA_RSPEC_RETRIED:-false}.json" + echo "Running e2e specs via command: '$QA_COMMAND'" + + if eval "$QA_COMMAND --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml"; then + echo "Test run finished successfully" + else + retry_failed_e2e_rspec_examples + fi +} + function retry_failed_e2e_rspec_examples() { local rspec_run_status=0 diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 9fc78d10cea..eeacf37edad 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -486,6 +486,8 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do page.within('.as-ci-cd') do fill_in 'plan_limits_ci_instance_level_variables', with: 5 + fill_in 'plan_limits_dotenv_size', with: 6 + fill_in 'plan_limits_dotenv_variables', with: 7 fill_in 'plan_limits_ci_pipeline_size', with: 10 fill_in 'plan_limits_ci_active_jobs', with: 20 fill_in 'plan_limits_ci_project_subscriptions', with: 30 @@ -498,6 +500,8 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do limits = default_plan.reload.limits expect(limits.ci_instance_level_variables).to eq(5) + expect(limits.dotenv_size).to eq(6) + expect(limits.dotenv_variables).to eq(7) expect(limits.ci_pipeline_size).to eq(10) expect(limits.ci_active_jobs).to eq(20) expect(limits.ci_project_subscriptions).to eq(30) diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index fc83169ba63..6713e0985eb 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -3,11 +3,17 @@ require 'spec_helper' RSpec.describe IdeHelper, feature_category: :web_ide do - describe '#ide_data' do - let_it_be(:project) { create(:project) } - let_it_be(:user) { project.creator } - let_it_be(:fork_info) { { ide_path: '/test/ide/path' } } + let_it_be(:project) { create(:project) } + let_it_be(:user) { project.creator } + before do + allow(helper).to receive(:current_user).and_return(user) + allow(helper).to receive(:content_security_policy_nonce).and_return('test-csp-nonce') + allow(helper).to receive(:new_session_path).and_return('test-sign-in-path') + end + + describe '#ide_data' do + let_it_be(:fork_info) { { ide_path: '/test/ide/path' } } let_it_be(:params) do { branch: 'master', @@ -26,12 +32,6 @@ RSpec.describe IdeHelper, feature_category: :web_ide do } end - before do - allow(helper).to receive(:current_user).and_return(user) - allow(helper).to receive(:content_security_policy_nonce).and_return('test-csp-nonce') - allow(helper).to receive(:new_session_path).and_return('test-sign-in-path') - end - it 'returns hash' do expect(helper.ide_data(project: nil, fork_info: fork_info, params: params)) .to include(base_data) @@ -141,4 +141,43 @@ RSpec.describe IdeHelper, feature_category: :web_ide do end end end + + describe '#show_web_ide_oauth_callback_mismatch_callout?' do + let_it_be(:oauth_application) { create(:oauth_application, owner: nil) } + + it 'returns false if Web IDE OAuth is not enabled' do + stub_feature_flags(vscode_web_ide: true, web_ide_oauth: false) + expect(helper.show_web_ide_oauth_callback_mismatch_callout?).to be false + end + + context 'when Web IDE OAuth is enabled' do + before do + stub_feature_flags(vscode_web_ide: true, web_ide_oauth: true) + end + + it 'returns false if no Web IDE OAuth application found' do + expect(helper.show_web_ide_oauth_callback_mismatch_callout?).to be false + end + + it "returns true if domain does not match OAuth application callback URLs" do + stub_application_setting({ web_ide_oauth_application: oauth_application }) + expect(helper.show_web_ide_oauth_callback_mismatch_callout?).to be true + end + + it "returns false if domain matches OAuth application callback URL" do + oauth_application.redirect_uri = "#{request.base_url}/oauth-redirect" + stub_application_setting({ web_ide_oauth_application: oauth_application }) + expect(helper.show_web_ide_oauth_callback_mismatch_callout?).to be false + end + end + end + + describe '#web_ide_oauth_application_id' do + let_it_be(:oauth_application) { create(:oauth_application, owner: nil) } + + it 'returns Web IDE OAuth application ID' do + stub_application_setting({ web_ide_oauth_application: oauth_application }) + expect(helper.web_ide_oauth_application_id).to eq(oauth_application.id) + end + end end diff --git a/spec/helpers/plan_limits_helper_spec.rb b/spec/helpers/plan_limits_helper_spec.rb index e87a48a1266..2ac362c5490 100644 --- a/spec/helpers/plan_limits_helper_spec.rb +++ b/spec/helpers/plan_limits_helper_spec.rb @@ -7,6 +7,8 @@ RSpec.describe PlanLimitsHelper, feature_category: :continuous_integration do it 'describes known limits', :aggregate_failures do [ :ci_instance_level_variables, + :dotenv_size, + :dotenv_variables, :ci_pipeline_size, :ci_active_jobs, :ci_project_subscriptions, diff --git a/spec/lib/api/entities/plan_limit_spec.rb b/spec/lib/api/entities/plan_limit_spec.rb index fdae28db635..96cbb150770 100644 --- a/spec/lib/api/entities/plan_limit_spec.rb +++ b/spec/lib/api/entities/plan_limit_spec.rb @@ -17,6 +17,8 @@ RSpec.describe API::Entities::PlanLimit do :ci_needs_size_limit, :ci_registered_group_runners, :ci_registered_project_runners, + :dotenv_size, + :dotenv_variables, :conan_max_file_size, :enforcement_limit, :generic_packages_max_file_size, diff --git a/spec/lib/banzai/filter/wiki_link_gollum_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_gollum_filter_spec.rb index d021bbca4cc..0124beb4a85 100644 --- a/spec/lib/banzai/filter/wiki_link_gollum_filter_spec.rb +++ b/spec/lib/banzai/filter/wiki_link_gollum_filter_spec.rb @@ -192,6 +192,14 @@ RSpec.describe Banzai::Filter::WikiLinkGollumFilter, feature_category: :wiki do end # rubocop:enable Layout/LineLength + context 'when the href gets sanitized out' do + it 'ignores the link' do + doc = pipeline_filter('[[test|http://]]', wiki: wiki) + + expect(doc.at_css('a')['data-gollum']).to be_nil + end + end + def pipeline_filter(text, context = {}) context = { project: project, no_sourcepos: true }.merge(context) diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index 4d335e8adcb..86ef06b43a0 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -593,6 +593,31 @@ RSpec.describe Integration, feature_category: :integrations do end end + describe '.create_from_default_integrations' do + let!(:instance_integration) { create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') } + let!(:instance_level_instance_specific_integration) { create(:beyond_identity_integration) } + + it 'creates integrations from default integrations' do + expect(described_class).to receive(:create_from_active_default_integrations) + .with(project, :project_id).and_call_original + expect(described_class).to receive(:create_from_default_instance_specific_integrations) + .with(project, :project_id).and_call_original + + expect(described_class.create_from_default_integrations(project, :project_id)).to eq(2) + end + + context 'when called with a group' do + it 'creates integrations from default integrations' do + expect(described_class).to receive(:create_from_active_default_integrations) + .with(group, :group_id).and_call_original + expect(described_class).to receive(:create_from_default_instance_specific_integrations) + .with(group, :group_id).and_call_original + + expect(described_class.create_from_default_integrations(group, :group_id)).to eq(2) + end + end + end + describe '.create_from_active_default_integrations' do context 'with an active instance-level integration' do let!(:instance_integration) { create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') } @@ -651,9 +676,10 @@ RSpec.describe Integration, feature_category: :integrations do end context 'with an active subgroup' do + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:project) { create(:project, group: subgroup) } + let!(:subgroup_integration) { create(:prometheus_integration, :group, group: subgroup, api_url: 'https://prometheus.subgroup.com/') } - let!(:subgroup) { create(:group, parent: group) } - let(:project) { create(:project, group: subgroup) } it 'creates an integration from the subgroup-level integration' do described_class.create_from_active_default_integrations(project, :project_id) @@ -701,6 +727,131 @@ RSpec.describe Integration, feature_category: :integrations do end end end + + context 'when the integration is instance specific' do + let!(:instance_integration) { create(:beyond_identity_integration) } + + it 'does not create an integration from the instance level instance specific integration' do + described_class.create_from_active_default_integrations(project, :project_id) + + expect(project.reload.integrations).to be_blank + end + end + end + end + + describe '.create_from_default_instance_specific_integrations' do + context 'with an active instance-level integration' do + let!(:instance_integration) { create(:beyond_identity_integration) } + + it 'creates an integration from the instance-level integration' do + described_class.create_from_default_instance_specific_integrations(project, :project_id) + expect(project.reload.integrations.size).to eq(1) + expect(project.reload.integrations.first.inherit_from_id).to eq(instance_integration.id) + end + + context 'when passing a group' do + it 'creates an integration from the instance-level integration' do + described_class.create_from_default_instance_specific_integrations(group, :group_id) + + expect(group.reload.integrations.size).to eq(1) + expect(group.reload.integrations.first.inherit_from_id).to eq(instance_integration.id) + end + end + + context 'with active group-level integration' do + let!(:group_integration) { create(:beyond_identity_integration, group: group, instance: false) } + + it 'creates an integration from the group-level integration' do + described_class.create_from_default_instance_specific_integrations(project, :project_id) + + expect(project.reload.integrations.size).to eq(1) + expect(project.reload.integrations.first.inherit_from_id).to eq(group_integration.id) + end + + context 'when group level integration is not active' do + let!(:group_integration) do + create(:beyond_identity_integration, group: group, instance: false, active: false) + end + + it 'creates an integration from the group-level integration' do + described_class.create_from_default_instance_specific_integrations(project, :project_id) + + expect(project.reload.integrations.size).to eq(1) + expect(project.reload.integrations.first.inherit_from_id).to eq(group_integration.id) + expect(project.reload.integrations.first).not_to be_active + end + end + + context 'when passing a group' do + let!(:subgroup) { create(:group, parent: group) } + + it 'creates an integration from the group-level integration' do + described_class.create_from_default_instance_specific_integrations(subgroup, :group_id) + + expect(subgroup.reload.integrations.size).to eq(1) + expect(subgroup.reload.integrations.first.inherit_from_id).to eq(group_integration.id) + end + end + + context 'with an active subgroup' do + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:project) { create(:project, group: subgroup) } + let!(:subgroup_integration) { create(:beyond_identity_integration, group: subgroup, instance: false) } + + it 'creates an integration from the subgroup-level integration' do + described_class.create_from_default_instance_specific_integrations(project, :project_id) + + expect(project.reload.integrations.size).to eq(1) + expect(project.reload.integrations.first.inherit_from_id).to eq(subgroup_integration.id) + end + + context 'when passing a group' do + let!(:sub_subgroup) { create(:group, parent: subgroup) } + + context 'with traversal queries' do + shared_examples 'correct ancestor order' do + it 'creates an integration from the subgroup-level integration' do + described_class.create_from_default_instance_specific_integrations(sub_subgroup, :group_id) + + sub_subgroup.reload + + expect(sub_subgroup.integrations.size).to eq(1) + expect(sub_subgroup.integrations.first.inherit_from_id).to eq(subgroup_integration.id) + end + + context 'when having an integration inheriting settings' do + let!(:subgroup_integration) do + create(:beyond_identity_integration, group: subgroup, inherit_from_id: group_integration.id, + instance: false) + end + + it 'creates an integration from the group-level integration' do + described_class.create_from_default_instance_specific_integrations(sub_subgroup, :group_id) + + sub_subgroup.reload + + expect(sub_subgroup.integrations.size).to eq(1) + expect(sub_subgroup.integrations.first.inherit_from_id).to eq(group_integration.id) + end + end + end + + include_examples 'correct ancestor order' + end + end + end + end + + context 'when the integration is not instance specific' do + let!(:instance_integration) { create(:prometheus_integration, :instance) } + + it 'does not create an integration from the instance level instance specific integration' do + described_class.create_from_default_instance_specific_integrations(project, :project_id) + + expect(project.reload.integrations).to be_blank + end + end end end @@ -1554,4 +1705,10 @@ RSpec.describe Integration, feature_category: :integrations do end end end + + describe '.instance_specific_integration_types' do + subject { described_class.instance_specific_integration_types } + + it { is_expected.to eq(['Integrations::BeyondIdentity']) } + end end diff --git a/spec/requests/api/admin/plan_limits_spec.rb b/spec/requests/api/admin/plan_limits_spec.rb index 8ea5b9eebda..bb019cc9b6d 100644 --- a/spec/requests/api/admin/plan_limits_spec.rb +++ b/spec/requests/api/admin/plan_limits_spec.rb @@ -24,6 +24,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d expect(json_response['ci_needs_size_limit']).to eq(Plan.default.actual_limits.ci_needs_size_limit) expect(json_response['ci_registered_group_runners']).to eq(Plan.default.actual_limits.ci_registered_group_runners) expect(json_response['ci_registered_project_runners']).to eq(Plan.default.actual_limits.ci_registered_project_runners) + expect(json_response['dotenv_size']).to eq(Plan.default.actual_limits.dotenv_size) + expect(json_response['dotenv_variables']).to eq(Plan.default.actual_limits.dotenv_variables) expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size) expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size) expect(json_response['helm_max_file_size']).to eq(Plan.default.actual_limits.helm_max_file_size) @@ -104,6 +106,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d ci_needs_size_limit: 106, ci_registered_group_runners: 107, ci_registered_project_runners: 108, + dotenv_size: 109, + dotenv_variables: 110, conan_max_file_size: 10, enforcement_limit: 100, generic_packages_max_file_size: 20, @@ -127,6 +131,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d expect(json_response['ci_needs_size_limit']).to eq(106) expect(json_response['ci_registered_group_runners']).to eq(107) expect(json_response['ci_registered_project_runners']).to eq(108) + expect(json_response['dotenv_size']).to eq(109) + expect(json_response['dotenv_variables']).to eq(110) expect(json_response['conan_max_file_size']).to eq(10) expect(json_response['enforcement_limit']).to eq(100) expect(json_response['generic_packages_max_file_size']).to eq(20) @@ -179,6 +185,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d ci_needs_size_limit: 'u', ci_registered_group_runners: 't', ci_registered_project_runners: 's', + dotenv_size: 'r', + dotenv_variables: 'q', conan_max_file_size: 'a', enforcement_limit: 'e', generic_packages_max_file_size: 'b', @@ -203,6 +211,8 @@ RSpec.describe API::Admin::PlanLimits, 'PlanLimits', feature_category: :shared d 'ci_needs_size_limit is invalid', 'ci_registered_group_runners is invalid', 'ci_registered_project_runners is invalid', + 'dotenv_size is invalid', + 'dotenv_variables is invalid', 'conan_max_file_size is invalid', 'enforcement_limit is invalid', 'generic_packages_max_file_size is invalid', diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 437a56b3cd3..9b70896ff67 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -1011,7 +1011,8 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi # - createdBy: Known N+1 issues, but only on exotic fields which we don't normally use # - ownerProject.pipeline: Needs arguments (iid or sha) # - project.productAnalyticsState: Can be requested only for 1 Project(s) at a time. - let(:excluded_fields) { %w[createdBy jobs pipeline productAnalyticsState] } + # - project.mergeTrains: Is a licensed feature + let(:excluded_fields) { %w[createdBy jobs pipeline productAnalyticsState mergeTrains] } it 'avoids N+1 queries', :use_sql_query_cache do discrete_runners_control = ActiveRecord::QueryRecorder.new(skip_cached: false) do diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb index 7bbfe1143a8..75763d34b28 100644 --- a/spec/requests/api/graphql/ci/runners_spec.rb +++ b/spec/requests/api/graphql/ci/runners_spec.rb @@ -51,7 +51,8 @@ RSpec.describe 'Query.runners', feature_category: :fleet_visibility do # Exclude fields from deeper objects which are problematic: # - ownerProject.pipeline: Needs arguments (iid or sha) # - project.productAnalyticsState: Can be requested only for 1 Project(s) at a time. - let(:excluded_fields) { %w[pipeline productAnalyticsState] } + # - mergeTrains Licensed feature + let(:excluded_fields) { %w[pipeline productAnalyticsState mergeTrains] } it 'returns expected runners' do post_graphql(query, current_user: current_user) diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb index 5d9c5050add..5450c06fb00 100644 --- a/spec/requests/api/graphql/group/container_repositories_spec.rb +++ b/spec/requests/api/graphql/group/container_repositories_spec.rb @@ -14,7 +14,7 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } - let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels] } + let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels mergeTrains] } let(:container_repositories_fields) do <<~GQL edges { @@ -156,7 +156,7 @@ RSpec.describe 'getting container repositories in a group', feature_category: :s it_behaves_like 'handling graphql network errors with the container registry' it_behaves_like 'not hitting graphql network errors with the container registry' do - let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels] } + let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels mergeTrains] } end it 'returns the total count of container repositories' do diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 20ad3d1965e..b51418c5e2a 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -16,7 +16,11 @@ RSpec.describe 'package details', feature_category: :package_registry do end let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles runners inboundAllowlistCount groupsAllowlistCount] } + let(:excluded) do + %w[metadata apiFuzzingCiConfiguration pipeline packageFiles + runners inboundAllowlistCount groupsAllowlistCount mergeTrains] + end + let(:metadata) { query_graphql_fragment('ComposerMetadata') } let(:package_files) { all_graphql_fields_for('PackageFile') } let(:package_global_id) { global_id_of(composer_package) } diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index 9a2d1c2317b..b9a5517ceb1 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } - let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels] } + let(:excluded_fields) { %w[pipeline jobs productAnalyticsState mlModels mergeTrains] } let(:container_repositories_fields) do <<~GQL edges { @@ -155,7 +155,7 @@ RSpec.describe 'getting container repositories in a project', feature_category: it_behaves_like 'handling graphql network errors with the container registry' it_behaves_like 'not hitting graphql network errors with the container registry' do - let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels] } + let(:excluded_fields) { %w[pipeline jobs tags tagsCount productAnalyticsState mlModels mergeTrains] } end it 'returns the total count of container repositories' do diff --git a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb index 4884e04ab23..45988db6e22 100644 --- a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb +++ b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb @@ -34,7 +34,7 @@ RSpec.describe 'getting notes for a merge request', feature_category: :code_revi notes { edges { node { - #{all_graphql_fields_for('Note', excluded: ['pipeline'])} + #{all_graphql_fields_for('Note', excluded: %w[pipeline mergeTrains])} } } } diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index 6e5ac0446d5..0b0320bb36c 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -25,8 +25,10 @@ RSpec.describe 'getting merge request information nested in a project', feature_ # codequalityReportsComparer because it is behind a feature flag # and runners because the user is not an admin and therefore has no access # and inboundAllowlistCount, groupsAllowlistCount the user has no access + # mergeTrains because it is a licensed feature let(:excluded) do - %w[jobs pipeline runners codequalityReportsComparer mlModels inboundAllowlistCount groupsAllowlistCount] + %w[jobs pipeline runners codequalityReportsComparer + mlModels inboundAllowlistCount groupsAllowlistCount mergeTrains] end let(:mr_fields) { all_graphql_fields_for('MergeRequest', excluded: excluded) } diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 1b0f654e7a8..4c2adadd360 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -64,7 +64,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor it 'executes a limited number of queries', :use_clean_rails_redis_caching do control = ActiveRecord::QueryRecorder.new { perform_archive_upload } - expect(control.count).to be <= 113 + expect(control.count).to be <= 114 end it 'schedules an import using a namespace' do diff --git a/spec/services/ci/update_build_names_service_spec.rb b/spec/services/ci/update_build_names_service_spec.rb index d3df3096234..9b20651b565 100644 --- a/spec/services/ci/update_build_names_service_spec.rb +++ b/spec/services/ci/update_build_names_service_spec.rb @@ -8,6 +8,7 @@ RSpec.describe Ci::UpdateBuildNamesService, feature_category: :continuous_integr let_it_be(:build1) { create(:ci_build, name: 'build1', pipeline: pipeline) } let_it_be(:build2) { create(:ci_build, name: 'build2', pipeline: pipeline) } let_it_be(:build3) { create(:ci_build, name: 'build3', pipeline: pipeline) } + let_it_be(:bridge1) { create(:ci_bridge, name: 'bridge1', pipeline: pipeline) } describe '#execute' do subject(:service) { described_class.new(pipeline) } @@ -54,5 +55,11 @@ RSpec.describe Ci::UpdateBuildNamesService, feature_category: :continuous_integr service.execute end + + it 'does not create build name for bridge job' do + service.execute + + expect(Ci::BuildName.find_by_build_id(bridge1.id)).to be_nil + end end end diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index f045feb2dca..9d2dab2e08f 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -326,6 +326,30 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_ end end + context 'when an instance-level instance specific integration' do + let_it_be(:instance_specific_integration) { create(:beyond_identity_integration) } + + it 'creates integration inheriting from the instance level integration' do + expect(created_group.integrations.count).to eq(1) + expect(created_group.integrations.last.active).to eq(instance_specific_integration.active) + expect(created_group.integrations.last.inherit_from_id).to eq(instance_specific_integration.id) + end + + context 'when there is a group-level exclusion' do + let(:extra_params) { { parent_id: group.id } } + let_it_be(:group) { create(:group) { |g| g.add_owner(user) } } + let_it_be(:group_integration) do + create(:beyond_identity_integration, group: group, instance: false, active: false) + end + + it 'creates a service from the group-level integration' do + expect(created_group.integrations.count).to eq(1) + expect(created_group.integrations.last.active).to eq(group_integration.active) + expect(created_group.integrations.last.inherit_from_id).to eq(group_integration.id) + end + end + end + context 'with an active instance-level integration' do let_it_be(:instance_integration) do create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb index f8697a5c95c..2aafdd48e74 100644 --- a/spec/services/groups/transfer_service_spec.rb +++ b/spec/services/groups/transfer_service_spec.rb @@ -365,6 +365,31 @@ RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :grou end end + context 'with instance specific integration' do + let_it_be(:instance_specific_integration) { create(:beyond_identity_integration) } + let_it_be(:group_instance_specific_integration) do + create( + :beyond_identity_integration, + group: group, + instance: false, + active: true, + inherit_from_id: instance_specific_integration.id + ) + end + + let_it_be(:parent_group_instance_specific_integration) do + create(:beyond_identity_integration, group: new_parent_group, instance: false, active: false) + end + + it 'replaces inherited integrations', :aggregate_failures do + new_group_instance_specific_integration = Integration.find_by(group: group, type: instance_specific_integration.type) + expect(new_group_instance_specific_integration.inherit_from_id).to eq(parent_group_instance_specific_integration.id) + expect(new_group_instance_specific_integration.active).to be_falsey + expect(PropagateIntegrationWorker).to have_received(:perform_async).with(new_group_instance_specific_integration.id) + expect(Integration.count).to eq(5) + end + end + context 'with a custom integration' do let_it_be(:group_integration) { create(:integrations_slack, :group, group: group, webhook: 'http://group.slack.com') } diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 79d428f69fe..11f3775ff5f 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -868,6 +868,41 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :groups_an describe 'create integration for the project' do subject(:project) { create_project(user, opts) } + context 'when an instance-level instance specific integration' do + let!(:instance_specific_integration) { create(:beyond_identity_integration) } + + it 'creates integration inheriting from the instance level integration' do + expect(project.integrations.count).to eq(1) + expect(project.integrations.first.active).to eq(instance_specific_integration.active) + expect(project.integrations.first.inherit_from_id).to eq(instance_specific_integration.id) + end + + context 'when there is a group-level exclusion' do + let(:opts) do + { + name: project_name, + namespace_id: group.id + } + end + + let!(:group) do + create(:group).tap do |group| + group.add_owner(user) + end + end + + let!(:group_integration) do + create(:beyond_identity_integration, group: group, instance: false, active: false) + end + + it 'creates a service from the group-level integration' do + expect(project.integrations.count).to eq(1) + expect(project.integrations.first.active).to eq(group_integration.active) + expect(project.integrations.first.inherit_from_id).to eq(group_integration.id) + end + end + end + context 'with an active instance-level integration' do let!(:instance_integration) { create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') } diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index bb4ced1a599..5929df3fc27 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -274,6 +274,29 @@ RSpec.describe Projects::TransferService, feature_category: :groups_and_projects expect { execute_transfer }.not_to change { project.slack_integration.webhook } end end + + context 'when the new default integration is instance specific and deactivated' do + let!(:instance_specific_integration) { create(:beyond_identity_integration) } + let!(:project_instance_specific_integration) do + create( + :beyond_identity_integration, + project: project, + instance: false, + active: true, + inherit_from_id: instance_specific_integration.id + ) + end + + let!(:group_instance_specific_integration) do + create(:beyond_identity_integration, group: target, instance: false, active: false) + end + + it 'creates an integration inheriting from the default' do + expect { execute_transfer } + .to change { project.beyond_identity_integration.reload.active }.from(true).to(false) + .and change { project.beyond_identity_integration.inherit_from_id }.to(group_instance_specific_integration.id) + end + end end context 'when project has pending builds', :sidekiq_inline do diff --git a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb index d7cfdc09732..bdc13fa8500 100644 --- a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb +++ b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'package details setup' do let(:package_global_id) { global_id_of(package) } let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } + let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles mergeTrains] } let(:package_files) { all_graphql_fields_for('PackageFile') } let(:dependency_links) { all_graphql_fields_for('PackageDependencyLink') } let(:pipelines) { all_graphql_fields_for('Pipeline', max_depth: 1) } diff --git a/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb b/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb index 2a62b097284..212ca994459 100644 --- a/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb +++ b/spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb @@ -16,6 +16,8 @@ RSpec.describe 'admin/application_settings/_ci_cd' do ci_needs_size_limit: 50, ci_registered_group_runners: 60, ci_registered_project_runners: 70, + dotenv_size: 80, + dotenv_variables: 90, pipeline_hierarchy_size: 300 } end @@ -44,6 +46,12 @@ RSpec.describe 'admin/application_settings/_ci_cd' do type: 'number') expect(page.find_field('Maximum number of Instance-level CI/CD variables that can be defined').value).to eq('5') + expect(rendered).to have_field('Maximum size of a dotenv artifact in bytes', type: 'number') + expect(page.find_field('Maximum size of a dotenv artifact in bytes').value).to eq('80') + + expect(rendered).to have_field('Maximum number of variables in a dotenv artifact', type: 'number') + expect(page.find_field('Maximum number of variables in a dotenv artifact').value).to eq('90') + expect(rendered).to have_field('Maximum number of jobs in a single pipeline', type: 'number') expect(page.find_field('Maximum number of jobs in a single pipeline').value).to eq('10')