diff --git a/.gitlab/ci/cng/main.gitlab-ci.yml b/.gitlab/ci/cng/main.gitlab-ci.yml
index 253a1d7bf17..b249b85cf95 100644
--- a/.gitlab/ci/cng/main.gitlab-ci.yml
+++ b/.gitlab/ci/cng/main.gitlab-ci.yml
@@ -15,7 +15,7 @@ include:
- local: .gitlab/ci/global.gitlab-ci.yml
.build-cng-env:
- image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}-alpine3.20
+ image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}-slim-bookworm
stage: prepare
needs:
# We need this job because we need its `cached-assets-hash.txt` artifact, so that we can pass the assets image tag to the downstream CNG pipeline.
@@ -23,19 +23,27 @@ include:
job: build-assets-image
variables:
BUILD_ENV: build.env
+ CNG_PROJECT_PATH: "${CI_PROJECT_NAMESPACE}/$[[ inputs.cng_path ]]"
+ CNG_SKIP_REDUNDANT_JOBS: "false"
+ CNG_VAR_SETUP_LOG_FILE: "tmp/build-cng-env.log"
before_script:
- source ./scripts/utils.sh
- install_gitlab_gem
script:
- 'ruby -r./scripts/trigger-build.rb -e "puts Trigger.variables_for_env_file(Trigger::CNG.new.variables)" > $BUILD_ENV'
- echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV
- - ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> $BUILD_ENV
+ - echo -e "section_start:`date +%s`:cng_var_setup_log[collapsed=true]\r\e[0KCNG Variables Script Log Output"
+ - cat $CNG_VAR_SETUP_LOG_FILE
+ - echo -e "section_end:`date +%s`:cng_var_setup_log\r\e[0K"
+ - echo -e "section_start:`date +%s`:build_env[collapsed=true]\r\e[0KBuild Environment Variables"
- cat $BUILD_ENV
+ - echo -e "section_end:`date +%s`:build_env\r\e[0K"
artifacts:
reports:
dotenv: $BUILD_ENV
paths:
- $BUILD_ENV
+ - $CNG_VAR_SETUP_LOG_FILE
expire_in: 7 days
when: always
@@ -66,11 +74,9 @@ include:
TOP_UPSTREAM_SOURCE_SHA: "${TOP_UPSTREAM_SOURCE_SHA}"
TOP_UPSTREAM_SOURCE_REF_SLUG: "${TOP_UPSTREAM_SOURCE_REF_SLUG}"
# prevent cache invalidation between pipeline runs
- CACHE_BUSTER: "false"
+ CACHE_BUSTER: "${CACHE_BUSTER}"
# link component version shas to current project instead of default CI_PIPELINE_CREATED_AT which forces rebuilds on each pipeline run
- CONTAINER_VERSION_SUFFIX: "${CI_PROJECT_NAME}"
- # skip no-op final-images-listing job
- SKIP_JOB_REGEX: /final-images-listing/
+ CONTAINER_VERSION_SUFFIX: "${CONTAINER_VERSION_SUFFIX}"
# disable buildx cluster while it's not supported on mirrors
DISABLE_BUILDX_CLUSTER: "true"
# disable external gem caching and rely on docker layer caching
@@ -79,9 +85,18 @@ include:
SKIP_IMAGE_SIGNING: "true"
SKIP_IMAGE_VERIFICATION: "true"
# set specific arch list
- ARCH_LIST: amd64
+ ARCH_LIST: "${ARCH_LIST}"
# use larger runner for complex rails build jobs
HIGH_CAPACITY_RUNNER_TAG: high-cpu
+ # skip no-op final-images-listing job or additionally jobs that don't produce new artifacts if skip jobs is enabled
+ SKIP_JOB_REGEX: "${SKIP_JOB_REGEX}"
+ # base images and additional args, these are required because jobs that set them might be skipped
+ DEBIAN_IMAGE: "${DEBIAN_IMAGE}"
+ DEBIAN_DIGEST: "${DEBIAN_DIGEST}"
+ DEBIAN_BUILD_ARGS: "${DEBIAN_BUILD_ARGS}"
+ ALPINE_IMAGE: "${ALPINE_IMAGE}"
+ ALPINE_DIGEST: "${ALPINE_DIGEST}"
+ ALPINE_BUILD_ARGS: "${ALPINE_BUILD_ARGS}"
trigger:
project: '${CI_PROJECT_NAMESPACE}/$[[ inputs.cng_path ]]'
branch: $TRIGGER_BRANCH
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 615f314472e..3e43846dae9 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -2378,7 +2378,7 @@
- !reference [".rails:rules:ee-gitlab-duo-chat-base", rules]
- <<: *if-merge-request
changes: *backend-patterns
- when: manual
+ when: never
allow_failure: true
.rails:rules:ee-gitlab-duo-chat-always:
@@ -2392,7 +2392,7 @@
- !reference [".rails:rules:ee-gitlab-duo-chat-optional", rules]
- <<: *if-default-branch-refs
changes: *setup-test-env-patterns
- when: manual
+ when: never
allow_failure: true
.rails:rules:db:check-schema:
diff --git a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml
index dfd3952782b..e8d759cb38e 100644
--- a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml
+++ b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml
@@ -35,6 +35,7 @@ workflow:
- .e2e-test-base
- .cng-qa-cache # cng-cache includes additional cached helm chart
needs:
+ - build-cng-env
- build-cng
tags:
- e2e
diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/graph_component.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/graph_component.vue
index f41cd787a72..b48536c5154 100644
--- a/app/assets/javascripts/ci/pipeline_details/graph/components/graph_component.vue
+++ b/app/assets/javascripts/ci/pipeline_details/graph/components/graph_component.vue
@@ -7,7 +7,6 @@ import LinksLayer from '../../../common/private/job_links_layer.vue';
import { DOWNSTREAM, MAIN, UPSTREAM, ONE_COL_WIDTH, STAGE_VIEW } from '../constants';
import { validateConfigPaths } from '../utils';
import LinkedGraphWrapper from './linked_graph_wrapper.vue';
-import LinkedPipelinesColumn from './linked_pipelines_column.vue';
import StageColumnComponent from './stage_column_component.vue';
export default {
@@ -15,7 +14,8 @@ export default {
components: {
LinksLayer,
LinkedGraphWrapper,
- LinkedPipelinesColumn,
+ LinkedPipelinesColumn: () =>
+ import(/* webpackChunkName: 'linked_pipelines_column' */ './linked_pipelines_column.vue'),
StageColumnComponent,
},
props: {
diff --git a/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipelines_column.vue b/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipelines_column.vue
index 9678b0213be..0e87ae19e8f 100644
--- a/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipelines_column.vue
+++ b/app/assets/javascripts/ci/pipeline_details/graph/components/linked_pipelines_column.vue
@@ -16,7 +16,7 @@ import LinkedPipeline from './linked_pipeline.vue';
export default {
components: {
LinkedPipeline,
- PipelineGraph: () => import('./graph_component.vue'),
+ PipelineGraph: () => import(/* webpackChunkName: 'pipeline_graph' */ './graph_component.vue'),
},
props: {
columnTitle: {
diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb
index 719809e4fd9..0ec4bf29afb 100644
--- a/app/controllers/admin/sessions_controller.rb
+++ b/app/controllers/admin/sessions_controller.rb
@@ -41,7 +41,7 @@ class Admin::SessionsController < ApplicationController
private
def user_is_admin!
- render_404 unless current_user&.admin?
+ render_404 unless current_user&.can_access_admin_area?
end
def two_factor_enabled_for_user?
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index 78f89d1aea7..ba985a98ba0 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -71,10 +71,7 @@ module SidebarsHelper
admin_mode_active: current_user_mode.admin_mode?,
enter_admin_mode_url: new_admin_session_path,
leave_admin_mode_url: destroy_admin_session_path,
- # Usually, using current_user.admin? is discouraged because it does not
- # check for admin mode, but since here we want to check admin? and admin mode
- # separately, we'll have to ignore the cop rule.
- user_is_admin: user.admin? # rubocop: disable Cop/UserAdmin
+ user_is_admin: user.can_access_admin_area?
},
avatar_url: user.avatar_url,
has_link_to_profile: current_user_menu?(:profile),
diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb
index 91479633d0d..51198615238 100644
--- a/app/models/concerns/editable.rb
+++ b/app/models/concerns/editable.rb
@@ -8,7 +8,7 @@ module Editable
end
def last_edited_by
- return if last_edited_at.blank?
+ return unless edited?
super || Users::Internal.ghost
end
diff --git a/app/models/concerns/notes/active_record.rb b/app/models/concerns/notes/active_record.rb
index ae89beb3271..42ebce4d553 100644
--- a/app/models/concerns/notes/active_record.rb
+++ b/app/models/concerns/notes/active_record.rb
@@ -5,13 +5,8 @@ module Notes
extend ActiveSupport::Concern
included do
- # Aliases to make application_helper#edited_time_ago_with_tooltip helper work properly with notes.
- # See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/10392/diffs#note_28719102
- alias_attribute :last_edited_by, :updated_by
-
belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User"
- belongs_to :last_edited_by, class_name: 'User'
has_many :todos
@@ -22,13 +17,21 @@ module Notes
validates :author, presence: true
validates :discussion_id, presence: true, format: { with: /\A\h{40}\z/ }
validate :validate_created_after
+ end
- def validate_created_after
- return unless created_at
- return if created_at >= '1970-01-01'
+ # Alias to make application_helper#edited_time_ago_with_tooltip helper work properly with notes.
+ # See https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/10392/diffs#note_28719102
+ def last_edited_by
+ updated_by
+ end
- errors.add(:created_at, s_('Note|The created date provided is too far in the past.'))
- end
+ private
+
+ def validate_created_after
+ return unless created_at
+ return if created_at >= '1970-01-01'
+
+ errors.add(:created_at, s_('Note|The created date provided is too far in the past.'))
end
end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 689cb6a1651..2d029801b59 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -456,7 +456,7 @@ class Note < ApplicationRecord
# Since we used `updated_at` as `last_edited_at`, it could be touched by transforming / resolving a note.
# This makes sure it is only marked as edited when the note body is updated.
def edited?
- return false if updated_by.blank?
+ return false if read_attribute(:last_edited_at).blank? && updated_by.blank?
super
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 9d1b444aab0..da2526feb84 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2605,6 +2605,10 @@ class User < ApplicationRecord
support_pin_data&.fetch(:expires_at, nil)
end
+ def can_access_admin_area?
+ admin?
+ end
+
protected
# override, from Devise::Validatable
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 21ada3bb99f..30dfbf3edf2 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -8,7 +8,7 @@ class BasePolicy < DeclarativePolicy::Base
next true if user_is_user? && @user.admin_bot?
if Gitlab::CurrentSettings.admin_mode
- Gitlab::Auth::CurrentUserMode.new(@user).admin_mode?
+ @user&.admin? && Gitlab::Auth::CurrentUserMode.new(@user).admin_mode?
else
@user&.admin?
end
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
index 8fa09683b06..59db709954c 100644
--- a/app/policies/concerns/policy_actor.rb
+++ b/app/policies/concerns/policy_actor.rb
@@ -21,6 +21,10 @@ module PolicyActor
false
end
+ def can_access_admin_area?
+ false
+ end
+
def external?
false
end
diff --git a/config/dependency_cruiser.js b/config/dependency_cruiser.js
index e511fc150cb..31efacd88c9 100644
--- a/config/dependency_cruiser.js
+++ b/config/dependency_cruiser.js
@@ -28,8 +28,6 @@ if (!process.env.DISABLE_EXCLUSIONS) {
// Merge request widget & tabs
'app/assets/javascripts/vue_merge_request_widget/components/checks/constants.js',
'app/assets/javascripts/merge_request_tabs.js',
- // CI pipeline
- 'app/assets/javascripts/ci/pipeline_details/graph/components/graph_component.vue',
// Nested Group projects list
'app/assets/javascripts/vue_shared/components/nested_groups_projects_list/nested_groups_projects_list_item.vue',
]);
@@ -65,6 +63,7 @@ module.exports = {
See https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#exclude-exclude-dependencies-from-being-cruised
*/
exclude: {
+ dynamic: true,
path: [],
},
// NOTE: This option is required to resolve aliases from the webpack config
diff --git a/config/feature_flags/ops/global_search_issues_tab.yml b/config/feature_flags/ops/global_search_issues_tab.yml
deleted file mode 100644
index 101b2588386..00000000000
--- a/config/feature_flags/ops/global_search_issues_tab.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: global_search_issues_tab
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68640
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339207
-milestone: '14.3'
-type: ops
-group: group::global search
-default_enabled: true
diff --git a/config/feature_flags/ops/global_search_merge_requests_tab.yml b/config/feature_flags/ops/global_search_merge_requests_tab.yml
deleted file mode 100644
index 7f4570e5134..00000000000
--- a/config/feature_flags/ops/global_search_merge_requests_tab.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: global_search_merge_requests_tab
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68640
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339207
-milestone: '14.3'
-type: ops
-group: group::global search
-default_enabled: true
diff --git a/config/feature_flags/ops/global_search_snippet_titles_tab.yml b/config/feature_flags/ops/global_search_snippet_titles_tab.yml
deleted file mode 100644
index e0b7422e0f3..00000000000
--- a/config/feature_flags/ops/global_search_snippet_titles_tab.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: global_search_snippet_titles_tab
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123668
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/415353
-milestone: '16.1'
-type: ops
-group: group::global search
-default_enabled: true
diff --git a/config/feature_flags/ops/global_search_users_tab.yml b/config/feature_flags/ops/global_search_users_tab.yml
deleted file mode 100644
index 57a07aa9e39..00000000000
--- a/config/feature_flags/ops/global_search_users_tab.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: global_search_users_tab
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84186
-rollout_issue_url:
-milestone: '14.10'
-type: ops
-group: group::global search
-default_enabled: true
diff --git a/db/migrate/20250109055316_migrate_global_search_settings_in_application_settings.rb b/db/migrate/20250109055316_migrate_global_search_settings_in_application_settings.rb
index aff522460c2..0b079c18034 100644
--- a/db/migrate/20250109055316_migrate_global_search_settings_in_application_settings.rb
+++ b/db/migrate/20250109055316_migrate_global_search_settings_in_application_settings.rb
@@ -9,36 +9,11 @@ class MigrateGlobalSearchSettingsInApplicationSettings < Gitlab::Database::Migra
end
def up
- ApplicationSetting.reset_column_information
-
- application_setting = ApplicationSetting.last
- return unless application_setting
-
- # rubocop:disable Gitlab/FeatureFlagWithoutActor -- Does not execute in user context
- search = {
- global_search_issues_enabled: Feature.enabled?(:global_search_issues_tab, type: :ops),
- global_search_merge_requests_enabled: Feature.enabled?(:global_search_merge_requests_tab, type: :ops),
- global_search_snippet_titles_enabled: Feature.enabled?(:global_search_snippet_titles_tab, type: :ops),
- global_search_users_enabled: Feature.enabled?(:global_search_users_tab, type: :ops)
- }
-
- if Gitlab.ee?
- search.merge!(
- global_search_code_enabled: Feature.enabled?(:global_search_code_tab, type: :ops),
- global_search_commits_enabled: Feature.enabled?(:global_search_commits_tab, type: :ops),
- global_search_epics_enabled: Feature.enabled?(:global_search_epics_tab, type: :ops),
- global_search_wiki_enabled: Feature.enabled?(:global_search_wiki_tab, type: :ops)
- )
- end
- # rubocop:enable Gitlab/FeatureFlagWithoutActor
-
- application_setting.update_columns(search: search, updated_at: Time.current)
+ # no-op this was just a data migration which is already done in 17.9. The plan is to remove the feature-flags used
+ # in this migration. So better to disable this migration in 17.11 to avoid any migration issues.
end
def down
- application_setting = ApplicationSetting.last
- return unless application_setting
-
- application_setting.update_column(:search, {})
+ # No op
end
end
diff --git a/doc/administration/broadcast_messages.md b/doc/administration/broadcast_messages.md
index 210da58e5e7..d8b5d0bb83b 100644
--- a/doc/administration/broadcast_messages.md
+++ b/doc/administration/broadcast_messages.md
@@ -8,7 +8,7 @@ title: Broadcast messages
{{< details >}}
- Tier: Free, Premium, Ultimate
-- Offering: GitLab Self-Managed
+- Offering: GitLab Self-Managed, GitLab Dedicated
{{< /details >}}
diff --git a/doc/user/gitlab_com/_index.md b/doc/user/gitlab_com/_index.md
index 83c87ea30a3..4d452d5366a 100644
--- a/doc/user/gitlab_com/_index.md
+++ b/doc/user/gitlab_com/_index.md
@@ -17,240 +17,6 @@ This page contains information about the settings that are used on GitLab.com, a
See some of these settings on the [instance configuration page](https://gitlab.com/help/instance_configuration) of GitLab.com.
-## Email confirmation
-
-GitLab.com has the:
-
-- [`email_confirmation_setting`](../../administration/settings/sign_up_restrictions.md#confirm-user-email)
- setting set to **Hard**.
-- [`unconfirmed_users_delete_after_days`](../../administration/moderate_users.md#automatically-delete-unconfirmed-users)
- setting set to three days.
-
-## Password requirements
-
-GitLab.com has the following requirements for passwords on new accounts and password changes:
-
-- Minimum character length 8 characters.
-- Maximum character length 128 characters.
-- All characters are accepted. For example, `~`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `()`,
- `[]`, `_`, `+`, `=`, and `-`.
-
-## SSH key restrictions
-
-GitLab.com uses the default [SSH key restrictions](../../security/ssh_keys_restrictions.md).
-
-## SSH host keys fingerprints
-
-Go to the current instance configuration to see the SSH host key fingerprints on
-GitLab.com.
-
-1. Sign in to GitLab.
-1. On the left sidebar, select **Help** ({{< icon name="question-o" >}}) > **Help**.
-1. On the Help page, select **Check the current instance configuration**.
-
-In the instance configuration, you see the **SSH host key fingerprints**:
-
-| Algorithm | MD5 (deprecated) | SHA256 |
-|------------------|------------------|---------|
-| ECDSA | `f1:d0:fb:46:73:7a:70:92:5a:ab:5d:ef:43:e2:1c:35` | `SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw` |
-| ED25519 | `2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16` | `SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8` |
-| RSA | `b6:03:0e:39:97:9e:d0:e7:24:ce:a3:77:3e:01:42:09` | `SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ` |
-
-The first time you connect to a GitLab.com repository, one of these keys is
-displayed in the output.
-
-## SSH `known_hosts` entries
-
-Add the following to `.ssh/known_hosts` to skip manual fingerprint
-confirmation in SSH:
-
-```plaintext
-gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
-gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
-gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
-```
-
-## Mail configuration
-
-GitLab.com sends emails from the `mg.gitlab.com` domain by using [Mailgun](https://www.mailgun.com/),
-and has its own dedicated IP addresses:
-
-- `23.253.183.236`
-- `69.72.35.190`
-- `69.72.44.107`
-- `159.135.226.146`
-- `161.38.202.219`
-- `192.237.158.143`
-- `192.237.159.239`
-- `198.61.254.136`
-- `198.61.254.160`
-- `209.61.151.122`
-
-The IP addresses for `mg.gitlab.com` are subject to change at any time.
-
-### Service Desk alias email address
-
-On GitLab.com, there's a mailbox configured for Service Desk with the email address:
-`contact-project+%{key}@incoming.gitlab.com`. To use this mailbox, configure the
-[custom suffix](../project/service_desk/configure.md#configure-a-suffix-for-service-desk-alias-email) in project
-settings.
-
-## Backups
-
-[See our backup strategy](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/#backups).
-
-To back up an entire project on GitLab.com, you can export it either:
-
-- [Through the UI](../project/settings/import_export.md).
-- [Through the API](../../api/project_import_export.md#schedule-an-export). You
- can also use the API to programmatically upload exports to a storage platform,
- such as Amazon S3.
-
-With exports, be aware of [what is and is not](../project/settings/import_export.md#project-items-that-are-exported)
-included in a project export.
-
-GitLab is built on Git, so you can back up just the repository of a project by cloning it to another computer.
-Similarly, you can clone a project's wiki to back it up. All files
-[uploaded after August 22, 2020](../project/wiki/_index.md#create-a-new-wiki-page)
-are included when cloning.
-
-## Delayed group deletion
-
-{{< details >}}
-
-- Tier: Premium, Ultimate
-- Offering: GitLab.com
-
-{{< /details >}}
-
-After May 08, 2023, all groups have delayed deletion enabled by default.
-
-Groups are permanently deleted after a seven-day delay.
-
-If you are on the Free tier, your groups are immediately deleted, and you will not be able to restore them.
-
-You can [view and restore groups marked for deletion](../group/_index.md#restore-a-group).
-
-## Delayed project deletion
-
-{{< details >}}
-
-- Tier: Premium, Ultimate
-- Offering: GitLab.com
-
-{{< /details >}}
-
-After May 08, 2023, all groups have delayed project deletion enabled by default.
-
-Projects are permanently deleted after a seven-day delay.
-
-If you are on the Free tier, your projects are immediately deleted, and you will not be able to restore them.
-
-You can [view and restore projects marked for deletion](../project/working_with_projects.md#restore-a-project).
-
-## Inactive project deletion
-
-[Inactive project deletion](../../administration/inactive_project_deletion.md) is disabled on GitLab.com.
-
-## Alternative SSH port
-
-GitLab.com can be reached by using a [different SSH port](https://about.gitlab.com/blog/2016/02/18/gitlab-dot-com-now-supports-an-alternate-git-plus-ssh-port/) for `git+ssh`.
-
-| Setting | Value |
-|------------|---------------------|
-| `Hostname` | `altssh.gitlab.com` |
-| `Port` | `443` |
-
-An example `~/.ssh/config` is the following:
-
-```plaintext
-Host gitlab.com
- Hostname altssh.gitlab.com
- User git
- Port 443
- PreferredAuthentications publickey
- IdentityFile ~/.ssh/gitlab
-```
-
-## GitLab Pages
-
-Some settings for [GitLab Pages](../project/pages/_index.md) differ from the
-[defaults for self-managed instances](../../administration/pages/_index.md):
-
-| Setting | GitLab.com |
-|:--------------------------------------------------|:-----------------------|
-| Domain name | `gitlab.io` |
-| IP address | `35.185.44.232` |
-| Support for custom domains | {{< icon name="check-circle" >}} Yes |
-| Support for TLS certificates | {{< icon name="check-circle" >}} Yes |
-| Maximum site size | 1 GB |
-| Number of custom domains for each GitLab Pages website | 150 |
-
-The maximum size of your Pages site depends on the maximum artifact size,
-which is part of [GitLab CI/CD](#gitlab-cicd).
-
-[Rate limits](#gitlabcom-specific-rate-limits) also exist for GitLab Pages.
-
-## GitLab container registry
-
-| Setting | GitLab.com | Default (self-managed) |
-|:---------------------------------------|:---------------------------------|------------------------|
-| Domain name | `registry.gitlab.com` | |
-| IP address | `35.227.35.254` | |
-| CDN domain name | `cdn.registry.gitlab-static.net` | |
-| CDN IP address | `34.149.22.116` | |
-| Authorization token duration (minutes) | `15` | See [increase container registry token duration](../../administration/packages/container_registry.md#increase-token-duration). |
-
-To use the GitLab container registry, Docker clients must have access to:
-
-- The registry endpoint and GitLab.com for authorization.
-- Google Cloud Storage or Google Cloud Content Delivery Network to download images.
-
-GitLab.com is fronted by Cloudflare.
-For incoming connections to GitLab.com, you must allow CIDR blocks of Cloudflare
-([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
-
-## GitLab CI/CD
-
-Below are the current settings regarding [GitLab CI/CD](../../ci/_index.md).
-Any settings or feature limits not listed here are using the defaults listed in
-the related documentation.
-
-| Setting | GitLab.com | Default (GitLab Self-Managed) |
-|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|------------------------|
-| Artifacts maximum size (compressed) | 1 GB | See [Maximum artifacts size](../../administration/settings/continuous_integration.md#maximum-artifacts-size). |
-| Artifacts [expiry time](../../ci/yaml/_index.md#artifactsexpire_in) | 30 days unless otherwise specified | See [Default artifacts expiration](../../administration/settings/continuous_integration.md#default-artifacts-expiration). Artifacts created before June 22, 2020 have no expiry. |
-| Scheduled Pipeline Cron | `*/5 * * * *` | See [Pipeline schedules advanced configuration](../../administration/cicd/_index.md#change-maximum-scheduled-pipeline-frequency). |
-| Maximum jobs in active pipelines | `500` for Free tier, `1000` for all trial tiers, `20000` for Premium, and `100000` for Ultimate. | See [Number of jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines). |
-| Maximum CI/CD subscriptions to a project | `2` | See [Number of CI/CD subscriptions to a project](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project). |
-| Maximum number of pipeline triggers in a project | `25000` | See [Limit the number of pipeline triggers](../../administration/instance_limits.md#limit-the-number-of-pipeline-triggers). |
-| Maximum pipeline schedules in projects | `10` for Free tier, `50` for all paid tiers | See [Number of pipeline schedules](../../administration/instance_limits.md#number-of-pipeline-schedules). |
-| Maximum pipelines for each schedule | `24` for Free tier, `288` for all paid tiers | See [Limit the number of pipelines created by a pipeline schedule each day](../../administration/instance_limits.md#limit-the-number-of-pipelines-created-by-a-pipeline-schedule-each-day). |
-| Maximum number of schedule rules defined for each security policy project | Unlimited for all paid tiers | See [Number of schedule rules defined for each security policy project](../../administration/instance_limits.md#limit-the-number-of-schedule-rules-defined-for-security-policy-project). |
-| Scheduled job archiving | 3 months | Never. Jobs created before June 22, 2020 were archived after September 22, 2020. |
-| Maximum test cases for each [unit test report](../../ci/testing/unit_test_reports.md) | `500000` | Unlimited. |
-| Maximum registered runners | Free tier: `50` for each group and `50`for each project
All paid tiers: `1000` for each group and `1000` for each project | See [Number of registered runners for each scope](../../administration/instance_limits.md#number-of-registered-runners-for-each-scope). |
-| Limit of dotenv variables | Free tier: `50`
Premium tier: `100`
Ultimate tier: `150` | See [Limit dotenv variables](../../administration/instance_limits.md#limit-dotenv-variables). |
-| Maximum downstream pipeline trigger rate (for a given project, user, and commit) | `350` each minute | See [Maximum downstream pipeline trigger rate](../../administration/settings/continuous_integration.md#maximum-downstream-pipeline-trigger-rate). |
-
-## Package registry limits
-
-The [maximum file size](../../administration/instance_limits.md#file-size-limits)
-for a package uploaded to the [GitLab package registry](../packages/package_registry/_index.md)
-varies by format:
-
-| Package type | GitLab.com |
-|------------------------|------------------------------------|
-| Conan | 5 GB |
-| Generic | 5 GB |
-| Helm | 5 MB |
-| Maven | 5 GB |
-| npm | 5 GB |
-| NuGet | 5 GB |
-| PyPI | 5 GB |
-| Terraform | 1 GB |
-| Machine learning model | 10 GB (uploads are capped at 5 GB) |
-
## Account and limit settings
GitLab.com has the following account limits enabled. If a setting is not listed,
@@ -280,104 +46,172 @@ this limit. Repository limits apply to both public and private projects.
{{< /alert >}}
-## Default import sources
+## Backups
-The [import sources](../project/import/_index.md#supported-import-sources) that are available to you by default depend on
-which GitLab you use:
+[See our backup strategy](https://handbook.gitlab.com/handbook/engineering/infrastructure/production/#backups).
-- GitLab.com: All available import sources are enabled by default.
-- GitLab Self-Managed: No import sources are enabled by default and must be
- [enabled](../../administration/settings/import_and_export_settings.md#configure-allowed-import-sources).
+To back up an entire project on GitLab.com, you can export it either:
-## Import placeholder user limits
+- [Through the UI](../project/settings/import_export.md).
+- [Through the API](../../api/project_import_export.md#schedule-an-export). You
+ can also use the API to programmatically upload exports to a storage platform,
+ such as Amazon S3.
-The number of [placeholder users](../project/import/_index.md#placeholder-users) created during an import on GitLab.com is limited for each top-level namespace. The limits
-differ depending on your plan and seat count.
-For more information, see the [table of placeholder user limits for GitLab.com](../project/import/_index.md#placeholder-user-limits).
+With exports, be aware of [what is and is not](../project/settings/import_export.md#project-items-that-are-exported)
+included in a project export.
-## IP range
+GitLab is built on Git, so you can back up just the repository of a project by cloning it to another computer.
+Similarly, you can clone a project's wiki to back it up. All files
+[uploaded after August 22, 2020](../project/wiki/_index.md#create-a-new-wiki-page)
+are included when cloning.
-GitLab.com uses the IP ranges `34.74.90.64/28` and `34.74.226.0/24` for traffic from its Web/API
-fleet. This whole range is solely allocated to GitLab. You can expect connections from webhooks or repository mirroring to come
-from those IPs and allow them.
+## Email confirmation
-GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
+GitLab.com has the:
-For outgoing connections from CI/CD runners, we are not providing static IP addresses.
-Most GitLab.com instance runners are deployed into Google Cloud in `us-east1`, except _Linux GPU-enabled_ and _Linux Arm64_, hosted in `us-central1`.
-You can configure any IP-based firewall by looking up
-[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).
-MacOS runners are hosted on AWS with runner managers hosted on Google Cloud. To configure IP-based firewall, you must allow both [AWS IP address ranges](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html) and [Google Cloud](https://cloud.google.com/compute/docs/faq#find_ip_range).
+- [`email_confirmation_setting`](../../administration/settings/sign_up_restrictions.md#confirm-user-email)
+ setting set to **Hard**.
+- [`unconfirmed_users_delete_after_days`](../../administration/moderate_users.md#automatically-delete-unconfirmed-users)
+ setting set to three days.
-## Hostname list
+## GitLab CI/CD
-Add these hostnames when you configure allow-lists in local HTTP(S) proxies,
-or other web-blocking software that governs end-user computers. Pages on
-GitLab.com load content from these hostnames:
+Below are the current settings regarding [GitLab CI/CD](../../ci/_index.md).
+Any settings or feature limits not listed here are using the defaults listed in
+the related documentation.
-- `gitlab.com`
-- `*.gitlab.com`
-- `*.gitlab-static.net`
-- `*.gitlab.io`
-- `*.gitlab.net`
+| Setting | GitLab.com | Default (GitLab Self-Managed) |
+|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------|------------------------|
+| Artifacts maximum size (compressed) | 1 GB | See [Maximum artifacts size](../../administration/settings/continuous_integration.md#maximum-artifacts-size). |
+| Artifacts [expiry time](../../ci/yaml/_index.md#artifactsexpire_in) | 30 days unless otherwise specified | See [Default artifacts expiration](../../administration/settings/continuous_integration.md#default-artifacts-expiration). Artifacts created before June 22, 2020 have no expiry. |
+| Scheduled Pipeline Cron | `*/5 * * * *` | See [Pipeline schedules advanced configuration](../../administration/cicd/_index.md#change-maximum-scheduled-pipeline-frequency). |
+| Maximum jobs in active pipelines | `500` for Free tier, `1000` for all trial tiers, `20000` for Premium, and `100000` for Ultimate. | See [Number of jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines). |
+| Maximum CI/CD subscriptions to a project | `2` | See [Number of CI/CD subscriptions to a project](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project). |
+| Maximum number of pipeline triggers in a project | `25000` | See [Limit the number of pipeline triggers](../../administration/instance_limits.md#limit-the-number-of-pipeline-triggers). |
+| Maximum pipeline schedules in projects | `10` for Free tier, `50` for all paid tiers | See [Number of pipeline schedules](../../administration/instance_limits.md#number-of-pipeline-schedules). |
+| Maximum pipelines for each schedule | `24` for Free tier, `288` for all paid tiers | See [Limit the number of pipelines created by a pipeline schedule each day](../../administration/instance_limits.md#limit-the-number-of-pipelines-created-by-a-pipeline-schedule-each-day). |
+| Maximum number of schedule rules defined for each security policy project | Unlimited for all paid tiers | See [Number of schedule rules defined for each security policy project](../../administration/instance_limits.md#limit-the-number-of-schedule-rules-defined-for-security-policy-project). |
+| Scheduled job archiving | 3 months | Never. Jobs created before June 22, 2020 were archived after September 22, 2020. |
+| Maximum test cases for each [unit test report](../../ci/testing/unit_test_reports.md) | `500000` | Unlimited. |
+| Maximum registered runners | Free tier: `50` for each group and `50`for each project
All paid tiers: `1000` for each group and `1000` for each project | See [Number of registered runners for each scope](../../administration/instance_limits.md#number-of-registered-runners-for-each-scope). |
+| Limit of dotenv variables | Free tier: `50`
Premium tier: `100`
Ultimate tier: `150` | See [Limit dotenv variables](../../administration/instance_limits.md#limit-dotenv-variables). |
+| Maximum downstream pipeline trigger rate (for a given project, user, and commit) | `350` each minute | See [Maximum downstream pipeline trigger rate](../../administration/settings/continuous_integration.md#maximum-downstream-pipeline-trigger-rate). |
-Documentation and Company pages served over `docs.gitlab.com` and `about.gitlab.com`
-also load certain page content directly from common public CDN hostnames.
+## GitLab container registry
-## Webhooks
+| Setting | GitLab.com | Default (self-managed) |
+|:---------------------------------------|:---------------------------------|------------------------|
+| Domain name | `registry.gitlab.com` | |
+| IP address | `35.227.35.254` | |
+| CDN domain name | `cdn.registry.gitlab-static.net` | |
+| CDN IP address | `34.149.22.116` | |
+| Authorization token duration (minutes) | `15` | See [increase container registry token duration](../../administration/packages/container_registry.md#increase-token-duration). |
-The following limits apply for [webhooks](../project/integrations/webhooks.md).
+To use the GitLab container registry, Docker clients must have access to:
-### Rate limits
+- The registry endpoint and GitLab.com for authorization.
+- Google Cloud Storage or Google Cloud Content Delivery Network to download images.
-For each top-level namespace, the number of times each minute that a webhook can be called.
-The limit varies depending on your plan and the number of seats in your subscription.
+GitLab.com is fronted by Cloudflare.
+For incoming connections to GitLab.com, you must allow CIDR blocks of Cloudflare
+([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
-| Plan | Default for GitLab.com |
-|----------------------|-------------------------|
-| Free | `500` |
-| Premium | `99` seats or fewer: `1,600`
`100-399` seats: `2,800`
`400` seats or more: `4,000` |
-| Ultimate and open source |`999` seats or fewer: `6,000`
`1,000-4,999` seats: `9,000`
`5,000` seats or more: `13,000` |
+## GitLab Pages
-### Other limits
+Some settings for [GitLab Pages](../project/pages/_index.md) differ from the
+[defaults for self-managed instances](../../administration/pages/_index.md):
-| Setting | Default for GitLab.com |
-|:--------------------------------------------------------------------|:-----------------------|
-| Number of webhooks | 100 for each project, 50 for each group (subgroup webhooks are not counted towards parent group limits ) |
-| Maximum payload size | 25 MB |
-| Timeout | 10 seconds |
-| [Parallel Pages deployments](../project/pages/parallel_deployments.md#limits) | 100 extra deployments (Premium tier), 500 extra deployments (Ultimate tier) |
+| Setting | GitLab.com |
+|:--------------------------------------------------|:-----------------------|
+| Domain name | `gitlab.io` |
+| IP address | `35.185.44.232` |
+| Support for custom domains | {{< icon name="check-circle" >}} Yes |
+| Support for TLS certificates | {{< icon name="check-circle" >}} Yes |
+| Maximum site size | 1 GB |
+| Number of custom domains for each GitLab Pages website | 150 |
-For self-managed instance limits, see:
+The maximum size of your Pages site depends on the maximum artifact size,
+which is part of [GitLab CI/CD](#gitlab-cicd).
-- [Webhook rate limit](../../administration/instance_limits.md#webhook-rate-limit).
-- [Number of webhooks](../../administration/instance_limits.md#number-of-webhooks).
-- [Webhook timeout](../../administration/instance_limits.md#webhook-timeout).
-- [Parallel Pages deployments](../../administration/instance_limits.md#number-of-parallel-pages-deployments).
+[Rate limits](#gitlabcom-specific-rate-limits) also exist for GitLab Pages.
-## GitLab-hosted runners
+## GitLab.com at scale
-You can use GitLab-hosted runners to run your CI/CD jobs on GitLab.com and GitLab Dedicated to seamlessly build, test, and deploy your application on different environments.
+In addition to the GitLab Enterprise Edition Linux package install, GitLab.com uses
+the following applications and settings to achieve scale. All settings are
+publicly available, as [Kubernetes configuration](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com)
+or [Chef cookbooks](https://gitlab.com/gitlab-cookbooks).
-For more information, see [GitLab-hosted runners](../../ci/runners/_index.md).
+### Elastic cluster
-## Puma
+We use Elasticsearch and Kibana for part of our monitoring solution:
-GitLab.com uses the default of 60 seconds for [Puma request timeouts](../../administration/operations/puma.md#change-the-worker-timeout).
+- [`gitlab-cookbooks` / `gitlab-elk` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-elk)
+- [`gitlab-cookbooks` / `gitlab_elasticsearch` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_elasticsearch)
-## Maximum number of reviewers and assignees
+### Fluentd
-{{< history >}}
+We use Fluentd to unify our GitLab logs:
-- Maximum assignees [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368936) in GitLab 15.6.
-- Maximum reviewers [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366485) in GitLab 15.9.
+- [`gitlab-cookbooks` / `gitlab_fluentd` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
-{{< /history >}}
+### Prometheus
-Merge requests enforce these maximums:
+Prometheus complete our monitoring stack:
-- Maximum assignees: 200
-- Maximum reviewers: 200
+- [`gitlab-cookbooks` / `gitlab-prometheus` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-prometheus)
+
+### Grafana
+
+For the visualization of monitoring data:
+
+- [`gitlab-cookbooks` / `gitlab-grafana` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-grafana)
+
+### Sentry
+
+Open source error tracking:
+
+- [`gitlab-cookbooks` / `gitlab-sentry` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-sentry)
+
+### Consul
+
+Service discovery:
+
+- [`gitlab-cookbooks` / `gitlab_consul` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_consul)
+
+### HAProxy
+
+High Performance TCP/HTTP Load Balancer:
+
+- [`gitlab-cookbooks` / `gitlab-haproxy` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-haproxy)
+
+## GitLab.com logging
+
+We use [Fluentd](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#fluentd)
+to parse our logs. Fluentd sends our logs to
+[Stackdriver Logging](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#stackdriver)
+and [Cloud Pub/Sub](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#cloud-pubsub).
+Stackdriver is used for storing logs long-term in Google Cold Storage (GCS).
+Cloud Pub/Sub is used to forward logs to an [Elastic cluster](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#elastic) using [`pubsubbeat`](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#pubsubbeat-vms).
+
+You can view more information in our runbooks such as:
+
+- A [detailed list of what we're logging](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#what-are-we-logging)
+- Our [current log retention policies](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#retention)
+- A [diagram of our logging infrastructure](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#logging-infrastructure-overview)
+
+### Job logs
+
+By default, GitLab does not expire job logs. Job logs are retained indefinitely,
+and can't be configured on GitLab.com to expire. You can erase job logs
+[manually with the Jobs API](../../api/jobs.md#erase-a-job) or by
+[deleting a pipeline](../../ci/pipelines/_index.md#delete-a-pipeline)
+
+## GitLab.com-specific Gitaly RPC concurrency limits
+
+Per-repository Gitaly RPC concurrency and queuing limits are configured for different types of Git operations such as `git clone`. When these limits are exceeded, a `fatal: remote error: GitLab is currently unable to handle this request due to load` message is returned to the client.
+
+For administrator documentation, see [limit RPC concurrency](../../administration/gitaly/concurrency_limiting.md#limit-rpc-concurrency).
## GitLab.com-specific rate limits
@@ -534,90 +368,98 @@ See [non-configurable limits](../../security/rate_limits.md#non-configurable-lim
for information on rate limits that are not configurable, and therefore also
used on GitLab.com.
-## GitLab.com-specific Gitaly RPC concurrency limits
+## GitLab-hosted runners
-Per-repository Gitaly RPC concurrency and queuing limits are configured for different types of Git operations such as `git clone`. When these limits are exceeded, a `fatal: remote error: GitLab is currently unable to handle this request due to load` message is returned to the client.
+You can use GitLab-hosted runners to run your CI/CD jobs on GitLab.com and GitLab Dedicated to seamlessly build, test, and deploy your application on different environments.
-For administrator documentation, see [limit RPC concurrency](../../administration/gitaly/concurrency_limiting.md#limit-rpc-concurrency).
+For more information, see [GitLab-hosted runners](../../ci/runners/_index.md).
-## GitLab.com logging
+## Hostname list
-We use [Fluentd](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#fluentd)
-to parse our logs. Fluentd sends our logs to
-[Stackdriver Logging](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#stackdriver)
-and [Cloud Pub/Sub](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#cloud-pubsub).
-Stackdriver is used for storing logs long-term in Google Cold Storage (GCS).
-Cloud Pub/Sub is used to forward logs to an [Elastic cluster](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#elastic) using [`pubsubbeat`](https://gitlab.com/gitlab-com/runbooks/tree/master/logging/doc#pubsubbeat-vms).
+Add these hostnames when you configure allow-lists in local HTTP(S) proxies,
+or other web-blocking software that governs end-user computers. Pages on
+GitLab.com load content from these hostnames:
-You can view more information in our runbooks such as:
+- `gitlab.com`
+- `*.gitlab.com`
+- `*.gitlab-static.net`
+- `*.gitlab.io`
+- `*.gitlab.net`
-- A [detailed list of what we're logging](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#what-are-we-logging)
-- Our [current log retention policies](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#retention)
-- A [diagram of our logging infrastructure](https://gitlab.com/gitlab-com/runbooks/-/tree/master/docs/logging#logging-infrastructure-overview)
+Documentation and Company pages served over `docs.gitlab.com` and `about.gitlab.com`
+also load certain page content directly from common public CDN hostnames.
-### Job logs
+## Imports
-By default, GitLab does not expire job logs. Job logs are retained indefinitely,
-and can't be configured on GitLab.com to expire. You can erase job logs
-[manually with the Jobs API](../../api/jobs.md#erase-a-job) or by
-[deleting a pipeline](../../ci/pipelines/_index.md#delete-a-pipeline).
+Settings related to importing data into GitLab.
-## GitLab.com at scale
+### Default import sources
-In addition to the GitLab Enterprise Edition Linux package install, GitLab.com uses
-the following applications and settings to achieve scale. All settings are
-publicly available, as [Kubernetes configuration](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com)
-or [Chef cookbooks](https://gitlab.com/gitlab-cookbooks).
+The [import sources](../project/import/_index.md#supported-import-sources) that are available to you by default depend on
+which GitLab you use:
-### Elastic cluster
+- GitLab.com: All available import sources are enabled by default.
+- GitLab Self-Managed: No import sources are enabled by default and must be
+ [enabled](../../administration/settings/import_and_export_settings.md#configure-allowed-import-sources).
-We use Elasticsearch and Kibana for part of our monitoring solution:
+### Import placeholder user limits
-- [`gitlab-cookbooks` / `gitlab-elk` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-elk)
-- [`gitlab-cookbooks` / `gitlab_elasticsearch` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_elasticsearch)
+The number of [placeholder users](../project/import/_index.md#placeholder-users) created during an import on GitLab.com is limited for each top-level namespace. The limits
+differ depending on your plan and seat count.
+For more information, see the [table of placeholder user limits for GitLab.com](../project/import/_index.md#placeholder-user-limits).
-### Fluentd
+## IP range
-We use Fluentd to unify our GitLab logs:
+GitLab.com uses the IP ranges `34.74.90.64/28` and `34.74.226.0/24` for traffic from its Web/API
+fleet. This whole range is solely allocated to GitLab. You can expect connections from webhooks or repository mirroring to come
+from those IPs and allow them.
-- [`gitlab-cookbooks` / `gitlab_fluentd` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
+GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
-### Prometheus
+For outgoing connections from CI/CD runners, we are not providing static IP addresses.
+Most GitLab.com instance runners are deployed into Google Cloud in `us-east1`, except _Linux GPU-enabled_ and _Linux Arm64_, hosted in `us-central1`.
+You can configure any IP-based firewall by looking up
+[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).
+MacOS runners are hosted on AWS with runner managers hosted on Google Cloud. To configure IP-based firewall, you must allow both [AWS IP address ranges](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html) and [Google Cloud](https://cloud.google.com/compute/docs/faq#find_ip_range).
-Prometheus complete our monitoring stack:
+## Mail configuration
-- [`gitlab-cookbooks` / `gitlab-prometheus` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-prometheus)
+GitLab.com sends emails from the `mg.gitlab.com` domain by using [Mailgun](https://www.mailgun.com/),
+and has its own dedicated IP addresses:
-### Grafana
+- `23.253.183.236`
+- `69.72.35.190`
+- `69.72.44.107`
+- `159.135.226.146`
+- `161.38.202.219`
+- `192.237.158.143`
+- `192.237.159.239`
+- `198.61.254.136`
+- `198.61.254.160`
+- `209.61.151.122`
-For the visualization of monitoring data:
+The IP addresses for `mg.gitlab.com` are subject to change at any time.
-- [`gitlab-cookbooks` / `gitlab-grafana` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-grafana)
+### Service Desk alias email address
-### Sentry
+On GitLab.com, there's a mailbox configured for Service Desk with the email address:
+`contact-project+%{key}@incoming.gitlab.com`. To use this mailbox, configure the
+[custom suffix](../project/service_desk/configure.md#configure-a-suffix-for-service-desk-alias-email) in project
+settings.
-Open source error tracking:
+## Maximum number of reviewers and assignees
-- [`gitlab-cookbooks` / `gitlab-sentry` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-sentry)
+{{< history >}}
-### Consul
+- Maximum assignees [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368936) in GitLab 15.6.
+- Maximum reviewers [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366485) in GitLab 15.9.
-Service discovery:
+{{< /history >}}
-- [`gitlab-cookbooks` / `gitlab_consul` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab_consul)
+Merge requests enforce these maximums:
-### HAProxy
-
-High Performance TCP/HTTP Load Balancer:
-
-- [`gitlab-cookbooks` / `gitlab-haproxy` · GitLab](https://gitlab.com/gitlab-cookbooks/gitlab-haproxy)
-
-## Sidekiq
-
-GitLab.com runs [Sidekiq](https://sidekiq.org) as an [external process](../../administration/sidekiq/_index.md)
-for Ruby job scheduling.
-
-The current settings are in the [GitLab.com Kubernetes pod configuration](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/blob/master/releases/gitlab/values/gprd.yaml.gotmpl).
+- Maximum assignees: 200
+- Maximum reviewers: 200
## Merge request limits
@@ -639,3 +481,174 @@ This feature is available for testing, but not ready for production use.
GitLab limits each merge request to 1000 [diff versions](../project/merge_requests/versions.md).
Merge requests that reach this limit cannot be updated further. Instead,
close the affected merge request and create a new merge request.
+
+## Password requirements
+
+GitLab.com has the following requirements for passwords on new accounts and password changes:
+
+- Minimum character length 8 characters.
+- Maximum character length 128 characters.
+- All characters are accepted. For example, `~`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `()`,
+ `[]`, `_`, `+`, `=`, and `-`.
+
+## Project and group deletion
+
+Settings related to the deletion of projects and groups.
+
+### Delayed group deletion
+
+{{< details >}}
+
+- Tier: Premium, Ultimate
+- Offering: GitLab.com
+
+{{< /details >}}
+
+After May 08, 2023, all groups have delayed deletion enabled by default.
+
+Groups are permanently deleted after a seven-day delay.
+
+If you are on the Free tier, your groups are immediately deleted, and you will not be able to restore them.
+
+You can [view and restore groups marked for deletion](../group/_index.md#restore-a-group).
+
+### Delayed project deletion
+
+{{< details >}}
+
+- Tier: Premium, Ultimate
+- Offering: GitLab.com
+
+{{< /details >}}
+
+After May 08, 2023, all groups have delayed project deletion enabled by default.
+
+Projects are permanently deleted after a seven-day delay.
+
+If you are on the Free tier, your projects are immediately deleted, and you will not be able to restore them.
+
+You can [view and restore projects marked for deletion](../project/working_with_projects.md#restore-a-project).
+
+### Inactive project deletion
+
+[Inactive project deletion](../../administration/inactive_project_deletion.md) is disabled on GitLab.com.
+
+## Package registry limits
+
+The [maximum file size](../../administration/instance_limits.md#file-size-limits)
+for a package uploaded to the [GitLab package registry](../packages/package_registry/_index.md)
+varies by format:
+
+| Package type | GitLab.com |
+|------------------------|------------------------------------|
+| Conan | 5 GB |
+| Generic | 5 GB |
+| Helm | 5 MB |
+| Maven | 5 GB |
+| npm | 5 GB |
+| NuGet | 5 GB |
+| PyPI | 5 GB |
+| Terraform | 1 GB |
+| Machine learning model | 10 GB (uploads are capped at 5 GB) |
+
+## Puma
+
+GitLab.com uses the default of 60 seconds for [Puma request timeouts](../../administration/operations/puma.md#change-the-worker-timeout).
+
+## Sidekiq
+
+GitLab.com runs [Sidekiq](https://sidekiq.org) as an [external process](../../administration/sidekiq/_index.md)
+for Ruby job scheduling.
+
+The current settings are in the [GitLab.com Kubernetes pod configuration](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/blob/master/releases/gitlab/values/gprd.yaml.gotmpl).
+
+## SSH keys and authentication
+
+Settings related to authentication with SSH. For information about maximum connections,
+see [SSH maximum number of connections](#ssh-maximum-number-of-connections).
+
+### Alternative SSH port
+
+GitLab.com can be reached by using a [different SSH port](https://about.gitlab.com/blog/2016/02/18/gitlab-dot-com-now-supports-an-alternate-git-plus-ssh-port/) for `git+ssh`.
+
+| Setting | Value |
+|------------|---------------------|
+| `Hostname` | `altssh.gitlab.com` |
+| `Port` | `443` |
+
+An example `~/.ssh/config` is the following:
+
+```plaintext
+Host gitlab.com
+ Hostname altssh.gitlab.com
+ User git
+ Port 443
+ PreferredAuthentications publickey
+ IdentityFile ~/.ssh/gitlab
+```
+
+### SSH host keys fingerprints
+
+Go to the current instance configuration to see the SSH host key fingerprints on
+GitLab.com.
+
+1. Sign in to GitLab.
+1. On the left sidebar, select **Help** ({{< icon name="question-o" >}}) > **Help**.
+1. On the Help page, select **Check the current instance configuration**.
+
+In the instance configuration, you see the **SSH host key fingerprints**:
+
+| Algorithm | MD5 (deprecated) | SHA256 |
+|------------------|------------------|---------|
+| ECDSA | `f1:d0:fb:46:73:7a:70:92:5a:ab:5d:ef:43:e2:1c:35` | `SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw` |
+| ED25519 | `2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16` | `SHA256:eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8` |
+| RSA | `b6:03:0e:39:97:9e:d0:e7:24:ce:a3:77:3e:01:42:09` | `SHA256:ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ` |
+
+The first time you connect to a GitLab.com repository, one of these keys is
+displayed in the output.
+
+### SSH key restrictions
+
+GitLab.com uses the default [SSH key restrictions](../../security/ssh_keys_restrictions.md).
+
+### SSH `known_hosts` entries
+
+Add the following to `.ssh/known_hosts` to skip manual fingerprint
+confirmation in SSH:
+
+```plaintext
+gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
+gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
+gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
+```
+
+## Webhooks
+
+The following limits apply for [webhooks](../project/integrations/webhooks.md).
+
+### Rate limits
+
+For each top-level namespace, the number of times each minute that a webhook can be called.
+The limit varies depending on your plan and the number of seats in your subscription.
+
+| Plan | Default for GitLab.com |
+|----------------------|-------------------------|
+| Free | `500` |
+| Premium | `99` seats or fewer: `1,600`
`100-399` seats: `2,800`
`400` seats or more: `4,000` |
+| Ultimate and open source |`999` seats or fewer: `6,000`
`1,000-4,999` seats: `9,000`
`5,000` seats or more: `13,000` |
+
+### Other limits
+
+| Setting | Default for GitLab.com |
+|:--------------------------------------------------------------------|:-----------------------|
+| Number of webhooks | 100 for each project, 50 for each group (subgroup webhooks are not counted towards parent group limits ) |
+| Maximum payload size | 25 MB |
+| Timeout | 10 seconds |
+| [Parallel Pages deployments](../project/pages/parallel_deployments.md#limits) | 100 extra deployments (Premium tier), 500 extra deployments (Ultimate tier) |
+
+For self-managed instance limits, see:
+
+- [Webhook rate limit](../../administration/instance_limits.md#webhook-rate-limit).
+- [Number of webhooks](../../administration/instance_limits.md#number-of-webhooks).
+- [Webhook timeout](../../administration/instance_limits.md#webhook-timeout).
+- [Parallel Pages deployments](../../administration/instance_limits.md#number-of-parallel-pages-deployments)..
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index 8a822fe0e02..50e4abdc755 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -19,7 +19,7 @@ design files, videos, and other non-text content.
GitLab supports two different types of file locking:
- [Exclusive file locks](../../topics/git/file_management.md#file-locks): Applied through the
- command line with Git LFS and `.gitattributes`.
+ command line with Git LFS and [`.gitattributes`](../../user/project/repository/files/git_attributes.md).
These locks prevent modifications to locked files on any branch.
- [Default branch file and directory locks](#default-branch-file-and-directory-locks): Applied
through the GitLab UI. These locks prevent modifications to files and directories on the
@@ -27,7 +27,7 @@ GitLab supports two different types of file locking:
## Permissions
-You can create file locks if you have at least the Developer role for the project.
+You must have at least the Developer role for the project to create, view, or manage file locks.
For more information, see [Roles and permissions](../../user/permissions.md).
## Default branch file and directory locks
@@ -60,6 +60,10 @@ to be aware of in-flight work without restricting their workflow on other branch
## Lock a file or directory
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
To lock a file or directory:
1. On the left sidebar, select **Search or go to** and find your project.
@@ -74,17 +78,41 @@ for locked files, see [issue 4623](https://gitlab.com/gitlab-org/gitlab/-/issues
## View and remove locks
-Locks can be removed by:
-
-- The user who created the lock.
-- Any user with at least the Maintainer role for the project.
-
-To view and manage file locks:
+To view locked files:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Code > Locked files**.
-This list displays all files locked either through Git LFS exclusive locks or the GitLab UI.
+The **Locked files** page displays all files locked with either Git LFS exclusive locks or the GitLab UI.
+
+Prerequisites:
+
+- You must be the user who created the lock.
+- You must have at least the Maintainer role for the project.
+
+To remove a lock:
+
+{{< tabs >}}
+
+{{< tab title="From a file" >}}
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. Go to the file you want to unlock.
+1. Select **Unlock**.
+1. On the confirmation dialog, select **Unlock**.
+
+{{< /tab >}}
+
+{{< tab title="From the Locked file page" >}}
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. Select **Code > Locked files**.
+1. To the right of the file you want to unlock, select **Unlock**.
+1. On the confirmation dialog, select **OK**.
+
+{{< /tab >}}
+
+{{< /tabs >}}
## Related topics
diff --git a/lib/gitlab/auth/current_user_mode.rb b/lib/gitlab/auth/current_user_mode.rb
index e102a444a43..695a2561f16 100644
--- a/lib/gitlab/auth/current_user_mode.rb
+++ b/lib/gitlab/auth/current_user_mode.rb
@@ -91,7 +91,7 @@ module Gitlab
def optionally_run_in_admin_mode(user)
raise NonSidekiqEnvironmentError unless Gitlab::Runtime.sidekiq?
- return yield unless Gitlab::CurrentSettings.admin_mode && user.admin?
+ return yield unless Gitlab::CurrentSettings.admin_mode && user.can_access_admin_area?
bypass_session!(user.id) do
with_current_admin(user) do
@@ -110,7 +110,7 @@ module Gitlab
return false unless user
Gitlab::SafeRequestStore.fetch(admin_mode_rs_key) do
- user.admin? && (privileged_runtime? || session_with_admin_mode?)
+ user.can_access_admin_area? && (privileged_runtime? || session_with_admin_mode?)
end
end
@@ -118,12 +118,12 @@ module Gitlab
return false unless user
Gitlab::SafeRequestStore.fetch(admin_mode_requested_rs_key) do
- user.admin? && admin_mode_requested_in_grace_period?
+ user.can_access_admin_area? && admin_mode_requested_in_grace_period?
end
end
def enable_admin_mode!(password: nil, skip_password_validation: false)
- return false unless user&.admin?
+ return false unless user&.can_access_admin_area?
return false unless skip_password_validation || user&.valid_password?(password)
raise NotRequestedError unless admin_mode_requested?
@@ -139,7 +139,7 @@ module Gitlab
end
def disable_admin_mode!
- return unless user&.admin?
+ return unless user&.can_access_admin_area?
reset_request_store_cache_entries
@@ -148,7 +148,7 @@ module Gitlab
end
def request_admin_mode!
- return unless user&.admin?
+ return unless user&.can_access_admin_area?
reset_request_store_cache_entries
diff --git a/package.json b/package.json
index 30bab874b60..3c94c364f2f 100644
--- a/package.json
+++ b/package.json
@@ -305,7 +305,7 @@
"swagger-cli": "^4.0.4",
"tailwindcss": "^3.4.1",
"timezone-mock": "^1.0.8",
- "vite": "^6.2.1",
+ "vite": "^6.2.2",
"vite-plugin-ruby": "^5.1.1",
"vue-loader-vue3": "npm:vue-loader@17.4.2",
"vue-test-utils-compat": "0.0.14",
diff --git a/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/deployment/default_values.rb b/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/deployment/default_values.rb
index 7b9bdd5bc77..22557d764f3 100644
--- a/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/deployment/default_values.rb
+++ b/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/deployment/default_values.rb
@@ -45,29 +45,29 @@ module Gitlab
# Key value pairs for ci specific component version values
#
- # This is defined as key value pairs to allow constructing example cli args for easier reproducability
+ # This is defined as key value pairs to allow constructing example cli args for easier reproducibility
#
# @return [Hash]
def component_ci_versions
{
"gitlab.gitaly.image.repository" => "#{IMAGE_REPOSITORY}/gitaly",
- "gitlab.gitaly.image.tag" => semver?(gitaly_version) ? "v#{gitaly_version}" : gitaly_version,
+ "gitlab.gitaly.image.tag" => with_semver_prefix(gitaly_version),
"gitlab.gitlab-shell.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-shell",
- "gitlab.gitlab-shell.image.tag" => "v#{gitlab_shell_version}",
+ "gitlab.gitlab-shell.image.tag" => with_semver_prefix(gitlab_shell_version),
"gitlab.migrations.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee",
- "gitlab.migrations.image.tag" => commit_sha,
+ "gitlab.migrations.image.tag" => toolbox_version,
"gitlab.toolbox.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee",
- "gitlab.toolbox.image.tag" => commit_sha,
+ "gitlab.toolbox.image.tag" => toolbox_version,
"gitlab.sidekiq.annotations.commit" => commit_short_sha,
"gitlab.sidekiq.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-sidekiq-ee",
- "gitlab.sidekiq.image.tag" => commit_sha,
+ "gitlab.sidekiq.image.tag" => sidekiq_version,
"gitlab.webservice.annotations.commit" => commit_short_sha,
"gitlab.webservice.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-webservice-ee",
- "gitlab.webservice.image.tag" => commit_sha,
+ "gitlab.webservice.image.tag" => webservice_version,
"gitlab.webservice.workhorse.image" => "#{IMAGE_REPOSITORY}/gitlab-workhorse-ee",
- "gitlab.webservice.workhorse.tag" => commit_sha,
+ "gitlab.webservice.workhorse.tag" => workhorse_version,
"gitlab.kas.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-kas",
- "gitlab.kas.image.tag" => semver?(kas_version) ? "v#{kas_version}" : kas_version
+ "gitlab.kas.image.tag" => with_semver_prefix(kas_version)
}
end
@@ -77,8 +77,10 @@ module Gitlab
#
# @param [String] version
# @return [Boolean]
- def semver?(version)
- version.match?(/^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?(-ee)?$/)
+ def with_semver_prefix(version)
+ return version unless version.match?(/^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?(-ee)?$/)
+
+ "v#{version}"
end
end
end
diff --git a/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/helpers/ci.rb b/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/helpers/ci.rb
index 3ab9dd776c4..a5a271af287 100644
--- a/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/helpers/ci.rb
+++ b/qa/gems/gitlab-orchestrator/lib/gitlab/orchestrator/lib/helpers/ci.rb
@@ -8,6 +8,10 @@ module Gitlab
module CI
extend self
+ def ci_project_dir
+ @ci_project_dir ||= ENV["CI_PROJECT_DIR"] || raise("CI_PROJECT_DIR is not set")
+ end
+
def commit_sha
@commit_sha ||= ENV["CI_COMMIT_SHA"] || raise("CI_COMMIT_SHA is not set")
end
@@ -17,19 +21,37 @@ module Gitlab
end
def gitaly_version
- @gitaly_version ||= File.read(File.join(ci_project_dir, "GITALY_SERVER_VERSION")).strip
+ @gitaly_version ||= ENV["GITALY_TAG"].presence || File.read(
+ File.join(ci_project_dir, "GITALY_SERVER_VERSION")
+ ).strip
end
def gitlab_shell_version
- @gitlab_shell_version ||= File.read(File.join(ci_project_dir, "GITLAB_SHELL_VERSION")).strip
+ @gitlab_shell_version ||= ENV["GITLAB_SHELL_TAG"].presence || File.read(
+ File.join(ci_project_dir, "GITLAB_SHELL_VERSION")
+ ).strip
+ end
+
+ def sidekiq_version
+ @sidekiq_version ||= ENV["GITLAB_SIDEKIQ_TAG"].presence || commit_sha
+ end
+
+ def toolbox_version
+ @toolbox_version ||= ENV["GITLAB_TOOLBOX_TAG"].presence || commit_sha
+ end
+
+ def webservice_version
+ @webservice_version ||= ENV["GITLAB_WEBSERVICE_TAG"].presence || commit_sha
+ end
+
+ def workhorse_version
+ @workhorse_version ||= ENV["GITLAB_WORKHORSE_TAG"].presence || commit_sha
end
def kas_version
- @kas_version ||= File.read(File.join(ci_project_dir, "GITLAB_KAS_VERSION")).strip
- end
-
- def ci_project_dir
- @ci_project_dir ||= ENV["CI_PROJECT_DIR"] || raise("CI_PROJECT_DIR is not set")
+ @kas_version ||= ENV["GITLAB_KAS_TAG"].presence || File.read(
+ File.join(ci_project_dir, "GITLAB_KAS_VERSION")
+ ).strip
end
end
end
diff --git a/qa/gems/gitlab-orchestrator/spec/unit/gitlab/orchestrator/deployment/default_values_spec.rb b/qa/gems/gitlab-orchestrator/spec/unit/gitlab/orchestrator/deployment/default_values_spec.rb
index 19739e29982..0845e3f240b 100644
--- a/qa/gems/gitlab-orchestrator/spec/unit/gitlab/orchestrator/deployment/default_values_spec.rb
+++ b/qa/gems/gitlab-orchestrator/spec/unit/gitlab/orchestrator/deployment/default_values_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
let(:gitaly_version) { "7aa06a578d76bdc294ee8e9acb4f063e7d9f1d5f" }
let(:kas_version) { "7aa06a578d76bdc294ee8e9acb4f063e7d9f1d5f" }
let(:shell_version) { "14.0.5" }
+ let(:image_tags) { {} }
let(:env) do
{
@@ -17,10 +18,21 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
}
end
+ let(:memoized_variables) do
+ [
+ :@ci_project_dir,
+ :@gitaly_version,
+ :@kas_version,
+ :@toolbox_version,
+ :@webservice_version,
+ :@workhorse_version,
+ :@gitlab_shell_version,
+ :@sidekiq_version
+ ]
+ end
+
before do
- described_class.instance_variable_set(:@ci_project_dir, nil)
- described_class.instance_variable_set(:@gitaly_version, nil)
- described_class.instance_variable_set(:@kas_version, nil)
+ memoized_variables.each { |variable| described_class.instance_variable_set(variable, nil) }
allow(File).to receive(:read).with(File.join(ci_project_dir, "GITALY_SERVER_VERSION")).and_return(gitaly_version)
allow(File).to receive(:read).with(File.join(ci_project_dir, "GITLAB_SHELL_VERSION")).and_return(shell_version)
@@ -28,7 +40,7 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
end
around do |example|
- ClimateControl.modify(env) { example.run }
+ ClimateControl.modify({ **env, **image_tags }) { example.run }
end
it "returns correct common values" do
@@ -91,4 +103,30 @@ RSpec.describe Gitlab::Orchestrator::Deployment::DefaultValues do
expect(described_class.component_ci_versions["gitlab.kas.image.tag"]).to eq("v#{kas_version}")
end
end
+
+ context "with explicitly provided image tags" do
+ let(:image_tags) do
+ {
+ "GITALY_TAG" => "13b6c124a0fe566c7e3db4477600e0f004ab69bc",
+ "GITLAB_SHELL_TAG" => "e6daa09dbb6ded5529224acdd1fd24000866aaaf",
+ "GITLAB_TOOLBOX_TAG" => "e6ce8d7f67c0787c706d5968a1f84c5e2d4f2368",
+ "GITLAB_SIDEKIQ_TAG" => "1088d209ac5dd8d245b00946de0760eb8fc9a181",
+ "GITLAB_WEBSERVICE_TAG" => "b0ccc088a766801c8db9e7c564ad28472f33916c",
+ "GITLAB_WORKHORSE_TAG" => "4a3990fb621ba6f6b7ddf36089868b24e22bb598",
+ "GITLAB_KAS_TAG" => "03faf0a4227405febb714c4eaa78e4f16f5d0a37"
+ }
+ end
+
+ it "uses explicitly provided image tags" do
+ expect(described_class.component_ci_versions).to include({
+ "gitlab.gitaly.image.tag" => image_tags["GITALY_TAG"],
+ "gitlab.gitlab-shell.image.tag" => image_tags["GITLAB_SHELL_TAG"],
+ "gitlab.toolbox.image.tag" => image_tags["GITLAB_TOOLBOX_TAG"],
+ "gitlab.sidekiq.image.tag" => image_tags["GITLAB_SIDEKIQ_TAG"],
+ "gitlab.webservice.image.tag" => image_tags["GITLAB_WEBSERVICE_TAG"],
+ "gitlab.webservice.workhorse.tag" => image_tags["GITLAB_WORKHORSE_TAG"],
+ "gitlab.kas.image.tag" => image_tags["GITLAB_KAS_TAG"]
+ })
+ end
+ end
end
diff --git a/scripts/trigger-build.rb b/scripts/trigger-build.rb
index 7d7745e3b12..5a63d3d9c94 100755
--- a/scripts/trigger-build.rb
+++ b/scripts/trigger-build.rb
@@ -5,6 +5,27 @@
#
# See https://docs.gitlab.com/ee/development/pipelines/internals.html#using-the-gitlab-ruby-gem-in-the-canonical-project.
require 'gitlab'
+require 'yaml'
+require 'json'
+require 'open3'
+require 'tempfile'
+require 'httparty'
+require 'logger'
+
+# Monkeypatch gitlab gem in order to increase per_page size when fetching registry repositories
+# rubocop:disable Style/ClassAndModuleChildren, Gitlab/NoCodeCoverageComment -- monkeypatch
+# :nocov:
+#
+# TODO: Remove this monkeypatch once https://github.com/NARKOZ/gitlab/pull/710 is part of a new gem release (currently v5.1.0 doens't contain it) and the release is used in this project.
+class Gitlab::Client
+ module ContainerRegistry
+ def registry_repositories(project, options = {})
+ get("/projects/#{url_encode project}/registry/repositories", query: options)
+ end
+ end
+end
+# :nocov:
+# rubocop:enable Style/ClassAndModuleChildren, Gitlab/NoCodeCoverageComment
module Trigger
def self.ee?
@@ -158,7 +179,7 @@ module Trigger
# Read version files from all components
def version_file_variables
- Dir.glob("*_VERSION").each_with_object({}) do |version_file, params| # rubocop:disable Rails/IndexWith
+ Dir.glob("*_VERSION").each_with_object({}) do |version_file, params| # rubocop:disable Rails/IndexWith -- Non-rails CI script
params[version_file] = version_param_value(version_file)
end
end
@@ -170,19 +191,77 @@ module Trigger
end
end
+ # Variable creation for downstream CNG build triggers
+ #
+ # This class additionally contains logic to check if component versions are already present in the container registry
+ # If they are, it adds those jobs to the SKIP_JOB_REGEX variable to skip them in the CNG build pipeline
+ #
+ # In order to correctly compute container versions and skip jobs, following actions are performed:
+ # * container version shell script is fetched from upstream
+ # * versions.yml file is fetched which contains most of variables required for container version calculation
+ # * image digest of stable Debian and Alpine images are fetched (alpine-stable and alpine-debian jobs functionality)
+ # * all container versions are computed using same logic as CNG build pipeline
+ # * registry is checked for image existence and appropriate jobs are added to skip regex pattern
+ #
class CNG < Base
ASSETS_HASH = "cached-assets-hash.txt"
+ DEFAULT_DEBIAN_IMAGE = "debian:bookworm-slim"
+ DEFAULT_ALPINE_IMAGE = "alpine:3.20"
+ DEFAULT_SKIPPED_JOBS = %w[final-images-listing].freeze
+ STABLE_BASE_JOBS = %w[alpine-stable debian-stable].freeze
def variables
+ hash = super.dup
# Delete variables that aren't useful when using native triggers.
- super.tap do |hash|
- hash.delete('TRIGGER_SOURCE')
- hash.delete('TRIGGERED_USER')
+ hash.delete('TRIGGER_SOURCE')
+ hash.delete('TRIGGERED_USER')
+
+ unless skip_redundant_jobs?
+ logger.info("Skipping redundant jobs is disabled, skipping existing container image check")
+ return hash
+ end
+
+ begin
+ hash.merge({
+ **deploy_component_tag_variables,
+ 'SKIP_JOB_REGEX' => skip_job_regex,
+ 'DEBIAN_IMAGE' => debian_image,
+ 'DEBIAN_DIGEST' => debian_image.split('@').last,
+ 'DEBIAN_BUILD_ARGS' => "--build-arg DEBIAN_IMAGE=#{ENV['GITLAB_DEPENDENCY_PROXY']}#{debian_image}",
+ 'ALPINE_IMAGE' => alpine_image,
+ 'ALPINE_DIGEST' => alpine_image.split('@').last,
+ 'ALPINE_BUILD_ARGS' => "--build-arg ALPINE_IMAGE=#{ENV['GITLAB_DEPENDENCY_PROXY']}#{alpine_image}"
+ })
+ rescue StandardError => e
+ logger.error("Error while calculating variables, err: #{e.message}")
+ logger.error(e.backtrace.join("\n"))
+ logger.error("Falling back to default variables")
+ hash
end
end
private
+ def logger
+ @logger ||= Logger.new(ENV["CNG_VAR_SETUP_LOG_FILE"] || "tmp/cng-var-setup.log")
+ end
+
+ def downstream_project_path
+ ENV.fetch('CNG_PROJECT_PATH', 'gitlab-org/build/CNG-mirror')
+ end
+
+ def skip_redundant_jobs?
+ ENV["CNG_SKIP_REDUNDANT_JOBS"] == "true"
+ end
+
+ def default_skip_job_regex
+ "/#{DEFAULT_SKIPPED_JOBS.join('|')}/"
+ end
+
+ def skip_job_regex
+ "/#{[*DEFAULT_SKIPPED_JOBS, *STABLE_BASE_JOBS, *skippable_jobs].join('|')}/"
+ end
+
def ref_param_name
'CNG_BRANCH'
end
@@ -205,20 +284,35 @@ module Trigger
end
end
+ def gitlab_version
+ ENV['CI_COMMIT_SHA']
+ end
+
def base_variables
- super.merge(
- 'GITLAB_REF_SLUG' => gitlab_ref_slug
- )
+ super.merge('GITLAB_REF_SLUG' => gitlab_ref_slug)
+ end
+
+ def default_build_vars
+ @default_build_vars ||= {
+ "CONTAINER_VERSION_SUFFIX" => ENV["CI_PROJECT_PATH_SLUG"] || "upstream-trigger",
+ "CACHE_BUSTER" => "false",
+ "ARCH_LIST" => ENV["ARCH_LIST"] || "amd64"
+ }
end
def extra_variables
{
"TRIGGER_BRANCH" => ref,
- "GITLAB_VERSION" => ENV['CI_COMMIT_SHA'],
+ "GITLAB_VERSION" => gitlab_version,
"GITLAB_TAG" => ENV['CI_COMMIT_TAG'], # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
"FORCE_RAILS_IMAGE_BUILDS" => 'true',
"CE_PIPELINE" => Trigger.ee? ? nil : "true", # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
- "EE_PIPELINE" => Trigger.ee? ? "true" : nil # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
+ "EE_PIPELINE" => Trigger.ee? ? "true" : nil, # Always set a value, even an empty string, so that the downstream pipeline can correctly check it.
+ "FULL_RUBY_VERSION" => RUBY_VERSION,
+ "SKIP_JOB_REGEX" => default_skip_job_regex,
+ "DEBIAN_IMAGE" => DEFAULT_DEBIAN_IMAGE, # Make sure default values are always set to not end up as empty string
+ "ALPINE_IMAGE" => DEFAULT_ALPINE_IMAGE, # Make sure default values are always set to not end up as empty string
+ **default_build_vars
}
end
@@ -238,6 +332,174 @@ module Trigger
raw_version
end
end
+
+ # Repository file tree in form of the output of `git ls-tree` command
+ #
+ # @return [String]
+ def repo_tree
+ logger.info("Fetching repo tree for ref '#{ref}'")
+ downstream_client
+ .repo_tree(downstream_project_path, ref: ref, per_page: 100).auto_paginate
+ .select { |node| node["type"] == "tree" }
+ .map { |node| "#{node['mode']} #{node['type']} #{node['id']} #{node['path']}" }
+ .join("\n")
+ end
+
+ # Script used for container version calculations in CNG build jobs
+ #
+ # @return [String]
+ def container_versions_script
+ logger.info("Fetching container versions script for ref '#{ref}'")
+ downstream_client.file_contents(
+ downstream_project_path,
+ "build-scripts/container_versions.sh",
+ ref
+ )
+ end
+
+ # Debian image with digest
+ #
+ # @return [String]
+ def debian_image
+ @debian_image ||= docker_image_with_digest(cng_versions["DEBIAN_IMAGE"])
+ end
+
+ # Alpine image with digest
+ #
+ # @return [String]
+ def alpine_image
+ @alpine_image ||= docker_image_with_digest(cng_versions["ALPINE_IMAGE"])
+ end
+
+ # Edition postfix
+ #
+ # @return [String]
+ def edition
+ @edition ||= Trigger.ee? ? "ee" : "ce"
+ end
+
+ # Component versions used in CNG builds
+ #
+ # @return [Hash]
+ def cng_versions
+ @cng_versions ||= YAML
+ .safe_load(downstream_client.file_contents(downstream_project_path, "ci_files/variables.yml", ref))
+ .fetch("variables")
+ end
+
+ # Environment variables required for container version fetching
+ # All these variables influence final container version values
+ #
+ # @return [Hash]
+ def version_fetch_env_variables
+ {
+ **cng_versions,
+ **version_file_variables,
+ **default_build_vars,
+ "GITLAB_VERSION" => gitlab_version,
+ "RUBY_VERSION" => RUBY_VERSION,
+ "DEBIAN_DIGEST" => debian_image.split("@").last,
+ "ALPINE_DIGEST" => alpine_image.split("@").last,
+ "REPOSITORY_TREE" => repo_tree
+ }
+ end
+
+ # Image tags used by CNG deployments
+ #
+ # @return [Hash]
+ def deploy_component_tag_variables
+ {
+ "GITALY_TAG" => container_versions["gitaly"],
+ "GITLAB_SHELL_TAG" => container_versions["gitlab-shell"],
+ "GITLAB_TOOLBOX_TAG" => container_versions["gitlab-toolbox-#{edition}"],
+ "GITLAB_SIDEKIQ_TAG" => container_versions["gitlab-sidekiq-#{edition}"],
+ "GITLAB_WEBSERVICE_TAG" => container_versions["gitlab-webservice-#{edition}"],
+ "GITLAB_WORKHORSE_TAG" => container_versions["gitlab-workhorse-#{edition}"],
+ "GITLAB_KAS_TAG" => container_versions["gitlab-kas"]
+ }
+ end
+
+ # Container versions for all components in CNG build pipeline
+ #
+ # @return [Hash]
+ def container_versions
+ @container_versions ||= Tempfile.create('container-versions') do |file|
+ file.write(container_versions_script)
+ file.close
+
+ build_vars = version_fetch_env_variables
+ logger.info("Computing container versions using following env variables:\n#{JSON.pretty_generate(build_vars)}")
+ out, status = Open3.capture2e(build_vars, "bash -c 'source #{file.path} && get_all_versions'")
+ raise "Failed to fetch container versions! #{out}" unless status.success?
+
+ component_versions = out.split("\n")
+ unless component_versions.all? { |line| line.match?(/^[A-Za-z0-9_\-]+=[^=]+$/) }
+ raise "Invalid container versions output format! Expected key=value pairs got:\n#{out}"
+ end
+
+ component_versions
+ .to_h { |entry| entry.split("=") }
+ .reject { |name, _version| Trigger.ee? ? name.end_with?("-ce") : name.end_with?("-ee") }
+ .tap { |versions| logger.info("Computed container versions:\n#{JSON.pretty_generate(versions)}") }
+ end
+ end
+
+ # List of jobs that can be skipped because tag is already present in the registry
+ #
+ # @return [Array]
+ def skippable_jobs
+ jobs = container_versions.keys
+ logger.info("Fetching container registry repositories for project '#{downstream_project_path}'")
+ repositories = downstream_client.registry_repositories(downstream_project_path, per_page: 100).auto_paginate
+ build_repositories = repositories.each_with_object({}) do |repo, hash|
+ job = jobs.find { |job| repo.name.end_with?(job) }
+ next unless job
+
+ hash[job] = repo.id
+ end
+ logger.info("Checking repositories (#{build_repositories.keys.join(', ')}) for existing tags")
+ existing_tags = container_versions.select do |job, tag|
+ downstream_client.registry_repository_tag(downstream_project_path, build_repositories[job], tag)
+ logger.info("Tag '#{tag}' exists in the registry, job '#{job}' will be skipped")
+ rescue Gitlab::Error::ResponseError => e
+ if e.is_a?(Gitlab::Error::NotFound)
+ logger.info("Tag '#{tag}' does not exist in the registry, job '#{job}' will not skipped")
+ else
+ logger.error("Failed to do a tag '#{tag}' lookup, err: #{e.message}, job '#{job}' will not be skipped")
+ end
+
+ false
+ end
+
+ existing_tags.keys
+ end
+
+ # rubocop:disable Gitlab/HTTParty -- CI script
+
+ # Fetch Docker image with digest from DockerHub
+ #
+ # @param docker_image [String]
+ # @return [String]
+ def docker_image_with_digest(docker_image)
+ image, tag = docker_image.split(":")
+
+ logger.info("Fetching digest for image '#{docker_image}'")
+ auth_url = "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/#{image}:pull"
+ auth_response = HTTParty.get(auth_url)
+ raise "Failed to get auth token" unless auth_response.success?
+
+ token = JSON.parse(auth_response.body)['token']
+ manifest_url = "https://registry.hub.docker.com/v2/library/#{image}/manifests/#{tag}"
+ response = HTTParty.head(manifest_url, headers: {
+ 'Authorization' => "Bearer #{token}",
+ 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json'
+ })
+ raise "Failed to fetch image '#{docker_image}' digest" unless response.success?
+
+ digest = response.headers['docker-content-digest'] || raise("Failed to get image digest")
+ "#{image}:#{tag}@#{digest}"
+ end
+ # rubocop:enable Gitlab/HTTParty -- CI script
end
# For GitLab documentation review apps
@@ -342,21 +604,25 @@ module Trigger
pipeline = super
project_path = variables['TOP_UPSTREAM_SOURCE_PROJECT']
merge_request_id = variables['TOP_UPSTREAM_MERGE_REQUEST_IID']
- comment = " \nStarted database testing [pipeline](https://ops.gitlab.net/#{downstream_project_path}/-/pipelines/#{pipeline.id}) " \
- "(limited access). This comment will be updated once the pipeline has finished running."
+ comment = <<~COMMENT.strip
+
+ Started database testing [pipeline](https://ops.gitlab.net/#{downstream_project_path}/-/pipelines/#{pipeline.id}) (limited access). This comment will be updated once the pipeline has finished running.
+ COMMENT
# Look for an existing note
- db_testing_notes = com_gitlab_client.merge_request_notes(project_path, merge_request_id).auto_paginate.select do |note|
- note.body.include?(IDENTIFIABLE_NOTE_TAG)
- end
+ db_testing_notes = com_gitlab_client
+ .merge_request_notes(project_path, merge_request_id)
+ .auto_paginate.select do |note|
+ note.body.include?(IDENTIFIABLE_NOTE_TAG)
+ end
- if db_testing_notes.empty?
- # This is the first note
- note = com_gitlab_client.create_merge_request_note(project_path, merge_request_id, comment)
+ return unless db_testing_notes.empty?
- puts "Posted comment to:\n"
- puts "https://gitlab.com/#{project_path}/-/merge_requests/#{merge_request_id}#note_#{note.id}"
- end
+ # This is the first note
+ note = com_gitlab_client.create_merge_request_note(project_path, merge_request_id, comment)
+
+ puts "Posted comment to:\n"
+ puts "https://gitlab.com/#{project_path}/-/merge_requests/#{merge_request_id}#note_#{note.id}"
end
private
diff --git a/spec/frontend/ci/pipeline_details/graph/components/graph_component_spec.js b/spec/frontend/ci/pipeline_details/graph/components/graph_component_spec.js
index ab4a948b1e7..c703a29703a 100644
--- a/spec/frontend/ci/pipeline_details/graph/components/graph_component_spec.js
+++ b/spec/frontend/ci/pipeline_details/graph/components/graph_component_spec.js
@@ -1,6 +1,6 @@
-import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import { LAYER_VIEW, STAGE_VIEW } from '~/ci/pipeline_details/graph/constants';
import PipelineGraph from '~/ci/pipeline_details/graph/components/graph_component.vue';
import JobItem from '~/ci/pipeline_details/graph/components/job_item.vue';
@@ -14,7 +14,6 @@ import { generateResponse, pipelineWithUpstreamDownstream } from '../mock_data';
describe('graph component', () => {
let wrapper;
- const findDownstreamColumn = () => wrapper.findByTestId('downstream-pipelines');
const findLinkedColumns = () => wrapper.findAllComponents(LinkedPipelinesColumn);
const findLinksLayer = () => wrapper.findComponent(LinksLayer);
const findStageColumns = () => wrapper.findAllComponents(StageColumnComponent);
@@ -43,7 +42,7 @@ describe('graph component', () => {
const createComponent = ({
data = {},
- mountFn = shallowMount,
+ mountFn = shallowMountExtended,
props = {},
stubOverride = {},
} = {}) => {
@@ -123,8 +122,10 @@ describe('graph component', () => {
});
describe('when linked pipelines are not present', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createComponent({ mountFn: mountExtended });
+
+ await nextTick();
});
it('should not render a linked pipelines column', () => {
@@ -133,11 +134,13 @@ describe('graph component', () => {
});
describe('when linked pipelines are present', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createComponent({
mountFn: mountExtended,
props: { pipeline: pipelineWithUpstreamDownstream(mockPipelineResponse) },
});
+
+ await nextTick();
});
it('should render linked pipelines columns', () => {
@@ -175,11 +178,13 @@ describe('graph component', () => {
});
});
- it('filters pipelines spawned from the same trigger job', () => {
- // The mock data has one downstream with `retried: true and one
- // with retried false. We filter the `retried: true` out so we
- // should only pass one downstream
- expect(findDownstreamColumn().props().linkedPipelines).toHaveLength(1);
+ it('filters pipelines spawned from the same trigger job', async () => {
+ const DownstreamColumn = (
+ await import('~/ci/pipeline_details/graph/components/linked_pipelines_column.vue')
+ ).default;
+
+ expect(wrapper.findComponent(DownstreamColumn).exists()).toBe(true);
+ expect(wrapper.findComponent(DownstreamColumn).props('linkedPipelines')).toHaveLength(1);
});
});
diff --git a/spec/frontend/ci/pipeline_details/graph/mock_data.js b/spec/frontend/ci/pipeline_details/graph/mock_data.js
index b75146fdd41..c60b2289752 100644
--- a/spec/frontend/ci/pipeline_details/graph/mock_data.js
+++ b/spec/frontend/ci/pipeline_details/graph/mock_data.js
@@ -1,3 +1,4 @@
+// Fixture located at spec/frontend/fixtures/pipeline_details.rb
import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
import { unwrapPipelineData } from '~/ci/pipeline_details/graph/utils';
import {
diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb
index 141220cac8f..fbf2b11329c 100644
--- a/spec/lib/gitlab/auth/current_user_mode_spec.rb
+++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb
@@ -124,141 +124,15 @@ RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store, feature_category:
context 'when the user is an admin' do
let(:user) { build_stubbed(:user, :admin) }
- context 'when admin mode not requested' do
- it 'is false by default' do
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'raises exception if we try to enable it' do
- expect do
- subject.enable_admin_mode!(password: user.password)
- end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
-
- expect(subject.admin_mode?).to be(false)
- end
- end
-
- context 'when admin mode requested first' do
- before do
- subject.request_admin_mode!
- end
-
- it 'is false by default' do
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'cannot be enabled with an invalid password' do
- subject.enable_admin_mode!(password: nil)
-
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password)
-
- expect(subject.admin_mode?).to be(true)
- end
-
- it 'can be disabled' do
- subject.enable_admin_mode!(password: user.password)
- subject.disable_admin_mode!
-
- expect(subject.admin_mode?).to be(false)
- end
-
- it 'will expire in the future' do
- subject.enable_admin_mode!(password: user.password)
- expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
-
- travel_to(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
- # in the future this will be a new request, simulate by clearing the RequestStore
- Gitlab::SafeRequestStore.clear!
-
- expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
- end
- end
-
- context 'skipping password validation' do
- it 'can be enabled with a valid password' do
- subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
-
- expect(subject.admin_mode?).to be(true)
- end
-
- it 'can be enabled with an invalid password' do
- subject.enable_admin_mode!(skip_password_validation: true)
-
- expect(subject.admin_mode?).to be(true)
- end
- end
-
- context 'with two independent sessions' do
- let(:another_session) { {} }
- let(:another_subject) { described_class.new(user) }
-
- before do
- allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
- end
-
- it 'cannot be enabled in one and seen in the other' do
- Gitlab::Session.with_session(another_session) do
- another_subject.request_admin_mode!
- another_subject.enable_admin_mode!(password: user.password)
- end
-
- expect(subject.admin_mode?).to be(false)
- end
- end
- end
-
- context 'bypassing session' do
- it 'is active by default' do
- described_class.bypass_session!(user.id) do
- expect(subject.admin_mode?).to be(true)
- end
- end
-
- it 'enable has no effect' do
- described_class.bypass_session!(user.id) do
- subject.request_admin_mode!
- subject.enable_admin_mode!(password: user.password)
-
- expect(subject.admin_mode?).to be(true)
- end
- end
-
- it 'disable has no effect' do
- described_class.bypass_session!(user.id) do
- subject.disable_admin_mode!
-
- expect(subject.admin_mode?).to be(true)
- end
- end
- end
+ it_behaves_like 'admin_mode? check if admin_mode can be enabled'
end
end
describe '#enable_admin_mode!' do
- let(:user) { build_stubbed(:user, :admin) }
+ context 'when the user is an admin' do
+ let(:user) { build_stubbed(:user, :admin) }
- it 'creates a timestamp in the session' do
- subject.request_admin_mode!
-
- subject.enable_admin_mode!(password: user.password)
-
- expect(session).to include(expected_session_entry(be_within(1.second).of(Time.now)))
- end
-
- it 'returns true after successful enable' do
- subject.request_admin_mode!
-
- expect(subject.enable_admin_mode!(password: user.password)).to eq(true)
- end
-
- it 'returns false after unsuccessful enable' do
- subject.request_admin_mode!
-
- expect(subject.enable_admin_mode!(password: 'wrong password')).to eq(false)
+ it_behaves_like 'enabling admin_mode when it can be enabled'
end
context 'when user is not an admin' do
@@ -270,25 +144,12 @@ RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store, feature_category:
expect(subject.enable_admin_mode!(password: user.password)).to eq(false)
end
end
-
- context 'when admin mode is not requested' do
- it 'raises error' do
- expect do
- subject.enable_admin_mode!(password: user.password)
- end.to raise_error(Gitlab::Auth::CurrentUserMode::NotRequestedError)
- end
- end
end
describe '#disable_admin_mode!' do
let(:user) { build_stubbed(:user, :admin) }
- it 'sets the session timestamp to nil' do
- subject.request_admin_mode!
- subject.disable_admin_mode!
-
- expect(session).to include(expected_session_entry(be_nil))
- end
+ it_behaves_like 'disabling admin_mode'
end
describe '.with_current_request_admin_mode' do
@@ -331,13 +192,6 @@ RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store, feature_category:
end
end
end
-
- def expected_session_entry(value_matcher)
- {
- Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
- Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher)
- }
- end
end
context 'when no session available' do
diff --git a/spec/migrations/20250109055316_migrate_global_search_settings_in_application_settings_spec.rb b/spec/migrations/20250109055316_migrate_global_search_settings_in_application_settings_spec.rb
deleted file mode 100644
index 1c8e51f9dfa..00000000000
--- a/spec/migrations/20250109055316_migrate_global_search_settings_in_application_settings_spec.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_migration!
-
-RSpec.describe MigrateGlobalSearchSettingsInApplicationSettings, feature_category: :global_search do
- let!(:application_setting) { table(:application_settings).create! }
-
- describe '#down' do
- let(:migration) { described_class.new }
-
- context 'when search settings is already set' do
- it 'does not update the search settings' do
- migration.up
- expect { migration.down }.to change { application_setting.reload.search }.to({})
- end
- end
- end
-
- describe '#up' do
- context 'when search is not already set' do
- before do
- stub_feature_flags(global_search_code_tab: false)
- stub_feature_flags(global_search_commits_tab: false)
- stub_feature_flags(global_search_issues_tab: false)
- end
-
- it 'migrates search from the feature flags in the application_settings successfully' do
- expected_search = if ::Gitlab.ee?
- {
- 'global_search_code_enabled' => false,
- 'global_search_commits_enabled' => false,
- 'global_search_epics_enabled' => true,
- 'global_search_issues_enabled' => false,
- 'global_search_merge_requests_enabled' => true,
- 'global_search_snippet_titles_enabled' => true,
- 'global_search_users_enabled' => true,
- 'global_search_wiki_enabled' => true
- }
- else
- {
- 'global_search_issues_enabled' => false,
- 'global_search_merge_requests_enabled' => true,
- 'global_search_snippet_titles_enabled' => true,
- 'global_search_users_enabled' => true
- }
- end
-
- expect { migrate! }.to change {
- application_setting.reload.search
- }.from({}).to(expected_search)
- end
- end
- end
-end
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index e0b797b3c89..085c52decc3 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -164,6 +164,12 @@ RSpec.describe DeployKey, :mailer, feature_category: :continuous_delivery do
it { expect(subject.can?(:push_code, project)).to be false }
end
end
+
+ describe '#can_access_admin_area?' do
+ it 'returns false' do
+ expect(subject.can_access_admin_area?).to be_falsey
+ end
+ end
end
describe '#audit_details' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index b3541801098..edb853f5b35 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -726,6 +726,56 @@ RSpec.describe Note, feature_category: :team_planning do
end
end
+ describe '#last_edited_by' do
+ let_it_be(:note, reload: true) do
+ create_timestamp = 1.day.ago
+ create(:note, created_at: create_timestamp, updated_at: create_timestamp)
+ end
+
+ let(:editor) { note.author }
+
+ context 'when the note is not edited' do
+ it 'returns nil' do
+ expect(note.last_edited_by).to be_nil
+ end
+ end
+
+ def update_note(note, **attributes)
+ # Update updated_at manually because of ThrottledTouch concern
+ note.update!(attributes.merge(updated_at: Time.current))
+ end
+
+ context 'with an edited note' do
+ before do
+ update_note(note, last_edited_at: Time.current, updated_by: editor)
+ end
+
+ it 'returns the updated_by user' do
+ expect(note.last_edited_by).to eq(editor)
+ end
+ end
+
+ context 'with an edited note by a deleted user' do
+ before do
+ update_note(note, last_edited_at: Time.current, updated_by: nil)
+ end
+
+ it 'returns the ghost user' do
+ expect(note.last_edited_by).to eq(Users::Internal.ghost)
+ end
+ end
+
+ context 'with a legacy edited note where last_edited_at is not set' do
+ before do
+ update_note(note, updated_by: editor)
+ end
+
+ it 'returns the updated_by user' do
+ expect(note.last_edited_by).to eq(editor)
+ end
+ end
+ end
+
describe '#confidential?' do
context 'when note is not confidential' do
context 'when include_noteable is set to true' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 22119896769..166e149fa18 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -6069,6 +6069,20 @@ RSpec.describe User, feature_category: :user_profile do
end
end
+ describe '#can_access_admin_area?' do
+ it 'returns false for regular user' do
+ user = build_stubbed(:user)
+
+ expect(user.can_access_admin_area?).to be_falsy
+ end
+
+ it 'returns true for admin user' do
+ user = build_stubbed(:user, :admin)
+
+ expect(user.can_access_admin_area?).to be_truthy
+ end
+ end
+
shared_examples 'organization owner' do
let!(:org_user) { create(:organization_user, organization: organization, user: user, access_level: access_level) }
diff --git a/spec/policies/base_policy_spec.rb b/spec/policies/base_policy_spec.rb
index d5e95625c9f..018ec0a80e3 100644
--- a/spec/policies/base_policy_spec.rb
+++ b/spec/policies/base_policy_spec.rb
@@ -85,6 +85,16 @@ RSpec.describe BasePolicy do
.to change { policy.allowed?(ability) }.from(false).to(true)
end
end
+
+ context 'with a limited admin user', :enable_admin_mode do
+ let(:current_user) { build_stubbed(:user) }
+
+ before do
+ allow(current_user).to receive(:can_access_admin_area?).and_return(true)
+ end
+
+ it { is_expected.not_to be_allowed(ability) }
+ end
end
describe 'read_dedicated_hosted_runner_usage' do
diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb
index 3cab64d191d..ac30a4f853b 100644
--- a/spec/requests/api/graphql/mutations/issues/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Update of an existing issue', feature_category: :team_planning d
context 'setting labels' do
before do
- allow(Gitlab::QueryLimiting::Transaction).to receive(:threshold).and_return(102)
+ allow(Gitlab::QueryLimiting::Transaction).to receive(:threshold).and_return(103)
end
let(:mutation) do
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
index cfaeb423e1b..cafc002bf44 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled, featur
context 'when the current user does not have permission to add assignees' do
let(:current_user) { create(:user) }
- let(:db_query_limit) { 31 }
+ let(:db_query_limit) { 32 }
it 'does not change the assignees' do
project.add_guest(current_user)
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index dfd86f95dac..06af787bb0d 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 <= 127
+ expect(control.count).to be <= 128
end
it 'schedules an import using a namespace' do
diff --git a/spec/requests/user_avatar_spec.rb b/spec/requests/user_avatar_spec.rb
index fd91ce274b3..8e462dd5cea 100644
--- a/spec/requests/user_avatar_spec.rb
+++ b/spec/requests/user_avatar_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe 'Loading a user avatar', feature_category: :user_profile do
get user.avatar_url # Skip queries on first application load
expect(response).to have_gitlab_http_status(:ok)
- expect { get user.avatar_url }.not_to exceed_query_limit(4)
+ expect { get user.avatar_url }.not_to exceed_query_limit(5)
end
end
diff --git a/spec/scripts/trigger-build_spec.rb b/spec/scripts/trigger-build_spec.rb
index 76276fd41dd..db433897be8 100644
--- a/spec/scripts/trigger-build_spec.rb
+++ b/spec/scripts/trigger-build_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# rubocop:disable RSpec/VerifiedDoubles
require 'fast_spec_helper'
@@ -21,7 +22,8 @@ RSpec.describe Trigger, feature_category: :tooling do
'GITLAB_USER_NAME' => 'gitlab_user_name',
'GITLAB_USER_LOGIN' => 'gitlab_user_login',
'QA_IMAGE' => 'qa_image',
- 'DOCS_PROJECT_API_TOKEN' => nil
+ 'DOCS_PROJECT_API_TOKEN' => nil,
+ 'CNG_SKIP_REDUNDANT_JOBS' => "false"
}
end
@@ -458,6 +460,25 @@ RSpec.describe Trigger, feature_category: :tooling do
end
end
end
+
+ describe "#extra_variables" do
+ before do
+ stub_env('CI_PROJECT_PATH_SLUG', 'project-path')
+ stub_env('ARCH_LIST', 'amd64,arm64')
+ end
+
+ it 'includes extra variables' do
+ expect(subject.variables).to include({
+ "FULL_RUBY_VERSION" => RUBY_VERSION,
+ "SKIP_JOB_REGEX" => "/final-images-listing/",
+ "DEBIAN_IMAGE" => "debian:bookworm-slim",
+ "ALPINE_IMAGE" => "alpine:3.20",
+ "CONTAINER_VERSION_SUFFIX" => "project-path",
+ "CACHE_BUSTER" => "false",
+ "ARCH_LIST" => 'amd64,arm64'
+ })
+ end
+ end
end
end
diff --git a/spec/services/issuable/callbacks/description_spec.rb b/spec/services/issuable/callbacks/description_spec.rb
index 98a7b5dadca..eaedcd76ed5 100644
--- a/spec/services/issuable/callbacks/description_spec.rb
+++ b/spec/services/issuable/callbacks/description_spec.rb
@@ -18,7 +18,9 @@ RSpec.describe Issuable::Callbacks::Description, feature_category: :portfolio_ma
project: project,
description: 'old description',
last_edited_at: Date.yesterday,
- last_edited_by: random_user
+ last_edited_by: random_user,
+ created_at: 3.days.ago,
+ updated_at: 3.days.ago
)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f39be2f1a23..9929f4f4b40 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -382,7 +382,7 @@ RSpec.configure do |config|
# See also spec/support/helpers/admin_mode_helpers.rb
if example.metadata[:enable_admin_mode] && !example.metadata[:do_not_mock_admin_mode]
allow_any_instance_of(Gitlab::Auth::CurrentUserMode).to receive(:admin_mode?) do |current_user_mode|
- current_user_mode.send(:user)&.admin?
+ current_user_mode.send(:user)&.can_access_admin_area?
end
end
diff --git a/spec/support/helpers/admin_mode_helpers.rb b/spec/support/helpers/admin_mode_helpers.rb
index 630c126adf4..1bc69ab4050 100644
--- a/spec/support/helpers/admin_mode_helpers.rb
+++ b/spec/support/helpers/admin_mode_helpers.rb
@@ -34,7 +34,7 @@ module AdminModeHelper
allow(Gitlab::Auth::CurrentUserMode).to receive(:new).and_call_original
allow(Gitlab::Auth::CurrentUserMode).to receive(:new).with(user).and_return(fake_user_mode)
- allow(fake_user_mode).to receive(:admin_mode?).and_return(user&.admin?)
+ allow(fake_user_mode).to receive(:admin_mode?).and_return(user&.can_access_admin_area?)
end
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/auth/current_user_mode_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/auth/current_user_mode_shared_examples.rb
new file mode 100644
index 00000000000..4244d30fdfe
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/auth/current_user_mode_shared_examples.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'admin_mode? check if admin_mode can be enabled' do
+ context 'when admin mode not requested' do
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
+
+ it 'raises exception if we try to enable it' do
+ expect do
+ subject.enable_admin_mode!(password: user.password)
+ end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
+
+ expect(subject.admin_mode?).to be(false)
+ end
+ end
+
+ context 'when admin mode requested first' do
+ before do
+ subject.request_admin_mode!
+ end
+
+ it 'is false by default' do
+ expect(subject.admin_mode?).to be(false)
+ end
+
+ it 'cannot be enabled with an invalid password' do
+ subject.enable_admin_mode!(password: nil)
+
+ expect(subject.admin_mode?).to be(false)
+ end
+
+ it 'can be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+
+ it 'can be disabled' do
+ subject.enable_admin_mode!(password: user.password)
+ subject.disable_admin_mode!
+
+ expect(subject.admin_mode?).to be(false)
+ end
+
+ it 'expires in the future' do
+ subject.enable_admin_mode!(password: user.password)
+ expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
+
+ travel_to(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
+ # in the future this will be a new request, simulate by clearing the RequestStore
+ Gitlab::SafeRequestStore.clear!
+
+ expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
+ end
+ end
+
+ context 'when skipping password validation' do
+ it 'can be enabled with a valid password' do
+ subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+
+ it 'can be enabled with an invalid password' do
+ subject.enable_admin_mode!(skip_password_validation: true)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ context 'with two independent sessions' do
+ let(:another_session) { {} }
+ let(:another_subject) { described_class.new(user) }
+
+ before do
+ allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
+ end
+
+ it 'cannot be enabled in one and seen in the other' do
+ Gitlab::Session.with_session(another_session) do
+ another_subject.request_admin_mode!
+ another_subject.enable_admin_mode!(password: user.password)
+ end
+
+ expect(subject.admin_mode?).to be(false)
+ end
+ end
+ end
+
+ context 'when bypassing session' do
+ it 'is active by default' do
+ described_class.bypass_session!(user.id) do
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ it 'enable has no effect' do
+ described_class.bypass_session!(user.id) do
+ subject.request_admin_mode!
+ subject.enable_admin_mode!(password: user.password)
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+
+ it 'disable has no effect' do
+ described_class.bypass_session!(user.id) do
+ subject.disable_admin_mode!
+
+ expect(subject.admin_mode?).to be(true)
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'enabling admin_mode when it can be enabled' do
+ it 'creates a timestamp in the session' do
+ subject.request_admin_mode!
+
+ subject.enable_admin_mode!(password: user.password)
+
+ expect(session).to include(expected_session_entry(be_within(1.second).of(Time.now)))
+ end
+
+ it 'returns true after successful enable' do
+ subject.request_admin_mode!
+
+ expect(subject.enable_admin_mode!(password: user.password)).to be(true)
+ end
+
+ it 'returns false after unsuccessful enable' do
+ subject.request_admin_mode!
+
+ expect(subject.enable_admin_mode!(password: 'wrong password')).to be(false)
+ end
+
+ context 'when admin mode is not requested' do
+ it 'raises error' do
+ expect do
+ subject.enable_admin_mode!(password: user.password)
+ end.to raise_error(Gitlab::Auth::CurrentUserMode::NotRequestedError)
+ end
+ end
+end
+
+RSpec.shared_examples 'disabling admin_mode' do
+ it 'sets the session timestamp to nil' do
+ subject.request_admin_mode!
+ subject.disable_admin_mode!
+
+ expect(session).to include(expected_session_entry(be_nil))
+ end
+end
+
+def expected_session_entry(value_matcher)
+ {
+ Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
+ Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher)
+ }
+end
diff --git a/yarn.lock b/yarn.lock
index 8f5425d25d3..0339baa23cb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15372,10 +15372,10 @@ vite-plugin-ruby@^5.1.1:
debug "^4.3.4"
fast-glob "^3.3.2"
-vite@^6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.1.tgz#ae865d4bb93a11844be1bc647c8b2dd1856ea180"
- integrity sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==
+vite@^6.2.2:
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.2.tgz#8098b12a6bfd95abe39399aa7d5faa56545d7a1a"
+ integrity sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==
dependencies:
esbuild "^0.25.0"
postcss "^8.5.3"