diff --git a/app/assets/javascripts/projects/pipelines/charts/format_utils.js b/app/assets/javascripts/projects/pipelines/charts/format_utils.js
new file mode 100644
index 00000000000..3a1f1469fc3
--- /dev/null
+++ b/app/assets/javascripts/projects/pipelines/charts/format_utils.js
@@ -0,0 +1,36 @@
+import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
+import { stringifyTime, parseSeconds } from '~/lib/utils/datetime/date_format_utility';
+import { formatNumber } from '~/locale';
+
+export const formatPipelineCount = (count) => {
+ try {
+ const n = BigInt(count);
+ return formatNumber(n);
+ } catch {
+ return '-';
+ }
+};
+
+export const formatPipelineCountPercentage = (a, b) => {
+ try {
+ // Dividing BigInt values loses the fractional part, multiply the numerator by a factor
+ // and then divide the result to keep digits of precision.
+ const factor = 1000; // 2 digits for percentage + 1 to round correctly
+ const an = BigInt(a);
+ const bn = BigInt(b);
+ const ratio = Number((BigInt(factor) * an) / bn) / factor;
+ if (Number.isFinite(ratio)) {
+ return getFormatter(SUPPORTED_FORMATS.percentHundred)(ratio * 100, 0);
+ }
+ } catch {
+ // return below
+ }
+ return '-';
+};
+
+export const formatPipelineDuration = (seconds) => {
+ if (Number.isFinite(seconds)) {
+ return stringifyTime(parseSeconds(seconds, { daysPerWeek: 7, hoursPerDay: 24 }));
+ }
+ return '-';
+};
diff --git a/app/assets/javascripts/vue_shared/components/crud_component.vue b/app/assets/javascripts/vue_shared/components/crud_component.vue
index 534ab730ee7..7ed29fe4c3c 100644
--- a/app/assets/javascripts/vue_shared/components/crud_component.vue
+++ b/app/assets/javascripts/vue_shared/components/crud_component.vue
@@ -192,7 +192,7 @@ export default {
:class="{ 'gl-mt-5': isCollapsible }"
>
id === selectedMilestoneId)
: null;
diff --git a/app/assets/javascripts/work_items/components/work_item_parent.vue b/app/assets/javascripts/work_items/components/work_item_parent.vue
index f6d75139f55..9ee3e96fe95 100644
--- a/app/assets/javascripts/work_items/components/work_item_parent.vue
+++ b/app/assets/javascripts/work_items/components/work_item_parent.vue
@@ -8,6 +8,12 @@ import updateParentMutation from '~/work_items/graphql/update_parent.mutation.gr
import { isValidURL } from '~/lib/utils/url_utility';
import updateNewWorkItemMutation from '~/work_items/graphql/update_new_work_item.mutation.graphql';
+import {
+ findMilestoneWidget,
+ findHierarchyWidgetDefinition,
+ isReference,
+ newWorkItemId,
+} from '~/work_items/utils';
import { updateParent } from '../graphql/cache_utils';
import groupWorkItemsQuery from '../graphql/group_work_items.query.graphql';
import projectWorkItemsQuery from '../graphql/project_work_items.query.graphql';
@@ -19,9 +25,9 @@ import {
NO_WORK_ITEM_IID,
sprintfWorkItem,
WORK_ITEM_TYPE_ENUM_EPIC,
+ WORK_ITEM_TYPE_NAME_EPIC,
WORK_ITEM_TYPE_NAME_ISSUE,
} from '../constants';
-import { findHierarchyWidgetDefinition, isReference, newWorkItemId } from '../utils';
export default {
linkId: uniqueId('work-item-parent-link-'),
@@ -86,6 +92,9 @@ export default {
isIssue() {
return this.workItemType === WORK_ITEM_TYPE_NAME_ISSUE;
},
+ isEpic() {
+ return this.workItemType === WORK_ITEM_TYPE_NAME_EPIC;
+ },
isLoading() {
return (
this.$apollo.queries.workspaceWorkItems.loading ||
@@ -108,6 +117,16 @@ export default {
parentWebUrl() {
return this.parent?.webUrl;
},
+ visibleWorkItems() {
+ return this.workItemsByReference.concat(this.workspaceWorkItems);
+ },
+ isSelectedParentAvailable() {
+ return this.localSelectedItem && this.visibleWorkItems.length;
+ },
+ selectedParentMilestone() {
+ const selectedParent = this.visibleWorkItems?.find(({ id }) => id === this.localSelectedItem);
+ return findMilestoneWidget(selectedParent)?.milestone || null;
+ },
showCustomNoneValue() {
return this.hasParent && this.parent === null;
},
@@ -124,6 +143,9 @@ export default {
this.localSelectedItem = newVal?.id;
},
},
+ localSelectedItem() {
+ if (this.isEpic) this.handleSelectedParentMilestone();
+ },
},
apollo: {
workspaceWorkItems: {
@@ -208,21 +230,18 @@ export default {
this.updateInProgress = true;
if (this.workItemId === newWorkItemId(this.workItemType)) {
- const visibleWorkItems = this.workItemsByReference.concat(this.workspaceWorkItems);
-
this.$apollo
.mutate({
mutation: updateNewWorkItemMutation,
variables: {
input: {
fullPath: this.fullPath,
- parent:
- this.localSelectedItem && visibleWorkItems.length
- ? {
- ...visibleWorkItems?.find(({ id }) => id === this.localSelectedItem),
- webUrl: this.parentWebUrl ?? null,
- }
- : null,
+ parent: this.isSelectedParentAvailable
+ ? {
+ ...this.visibleWorkItems.find(({ id }) => id === this.localSelectedItem),
+ webUrl: this.parentWebUrl ?? null,
+ }
+ : null,
workItemType: this.workItemType,
},
},
@@ -294,6 +313,9 @@ export default {
this.searchStarted = false;
this.searchTerm = '';
},
+ handleSelectedParentMilestone() {
+ this.$emit('parentMilestone', this.selectedParentMilestone);
+ },
},
};
diff --git a/app/assets/javascripts/work_items/graphql/group_work_items.query.graphql b/app/assets/javascripts/work_items/graphql/group_work_items.query.graphql
index a6ebf4424c8..b87a2298c70 100644
--- a/app/assets/javascripts/work_items/graphql/group_work_items.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/group_work_items.query.graphql
@@ -32,6 +32,18 @@ query groupWorkItems(
name
iconName
}
+ widgets {
+ type
+ ... on WorkItemWidgetMilestone {
+ milestone {
+ id
+ dueDate
+ startDate
+ title
+ webPath
+ }
+ }
+ }
}
}
workItemsByIid: workItems(
diff --git a/app/controllers/projects/settings/packages_and_registries_controller.rb b/app/controllers/projects/settings/packages_and_registries_controller.rb
index 8aa608d1685..0219102769b 100644
--- a/app/controllers/projects/settings/packages_and_registries_controller.rb
+++ b/app/controllers/projects/settings/packages_and_registries_controller.rb
@@ -9,6 +9,8 @@ module Projects
before_action :packages_and_registries_settings_enabled!
before_action :set_feature_flag_packages_protected_packages, only: :show
before_action :set_feature_flag_container_registry_protected_containers_delete, only: :show
+ before_action :set_feature_flag_container_registry_immutable_tags, only: :show
+ before_action :push_create_container_registry_protection_immutable_tag_rule_ability, only: :show
feature_category :package_registry
urgency :low
@@ -40,6 +42,15 @@ module Projects
def set_feature_flag_container_registry_protected_containers_delete
push_frontend_feature_flag(:container_registry_protected_containers_delete, project)
end
+
+ def set_feature_flag_container_registry_immutable_tags
+ push_frontend_feature_flag(:container_registry_immutable_tags, project)
+ end
+
+ def push_create_container_registry_protection_immutable_tag_rule_ability
+ push_frontend_ability(ability: :create_container_registry_protection_immutable_tag_rule,
+ resource: project, user: current_user)
+ end
end
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index d9e748d7b27..bb38799844a 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -267,6 +267,7 @@ module ApplicationSettingsHelper
:auto_devops_domain,
:autocomplete_users_limit,
:autocomplete_users_unauthenticated_limit,
+ :ci_delete_pipelines_in_seconds_limit_human_readable,
:ci_job_live_trace_enabled,
:ci_partitions_size_limit,
:concurrent_github_import_jobs_limit,
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 385860e5d04..fda2f11fbae 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -523,10 +523,15 @@ class ApplicationSetting < ApplicationRecord
jsonb_accessor :ci_cd_settings,
ci_job_live_trace_enabled: [:boolean, { default: false }],
- ci_partitions_size_limit: [::Gitlab::Database::Type::JsonbInteger.new, { default: 100.gigabytes }]
+ ci_partitions_size_limit: [::Gitlab::Database::Type::JsonbInteger.new, { default: 100.gigabytes }],
+ ci_delete_pipelines_in_seconds_limit: [:integer, { default: ChronicDuration.parse('1 year') }]
+
+ chronic_duration_attr :ci_delete_pipelines_in_seconds_limit_human_readable, :ci_delete_pipelines_in_seconds_limit
validate :validate_object_storage_for_live_trace_configuration, if: -> { ci_job_live_trace_enabled? }
validates :ci_partitions_size_limit, presence: true, numericality: { only_integer: true, greater_than: 0 }
+ validates :ci_delete_pipelines_in_seconds_limit, presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 1.day }
validates :default_ci_config_path,
format: { without: %r{(\.{2}|\A/)}, message: N_('cannot include leading slash or directory traversal.') },
@@ -1154,6 +1159,11 @@ class ApplicationSetting < ApplicationRecord
self.class.database.cached_column_exists?(:failed_login_attempts_unlock_period_in_minutes)
end
+ def ci_delete_pipelines_in_seconds_limit_human_readable_long
+ value = ci_delete_pipelines_in_seconds_limit
+ ChronicDuration.output(value, format: :long) if value
+ end
+
private
def parsed_grafana_url
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index cd71001f8ea..ceed2a91eac 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -49,6 +49,7 @@ module ApplicationSettingImplementation
ci_job_live_trace_enabled: false,
ci_max_total_yaml_size_bytes: 314572800, # max_yaml_size_bytes * ci_max_includes = 2.megabyte * 150
ci_partitions_size_limit: 100.gigabytes,
+ ci_delete_pipelines_in_seconds_limit_human_readable: '1 year',
commit_email_hostname: default_commit_email_hostname,
container_expiration_policies_enable_historic_entries: false,
container_registry_features: [],
diff --git a/app/models/ci/job_token/allowlist_migration_task.rb b/app/models/ci/job_token/allowlist_migration_task.rb
index 59b61435753..2b20a6e43d9 100644
--- a/app/models/ci/job_token/allowlist_migration_task.rb
+++ b/app/models/ci/job_token/allowlist_migration_task.rb
@@ -10,14 +10,15 @@ module Ci
INPUT_ID_LIMIT = 1000
- def initialize(only_ids: nil, exclude_ids: nil, preview: nil, user: nil, output_stream: $stdout)
+ def initialize(only_ids: nil, exclude_ids: nil, preview: nil, user: nil, output_stream: $stdout, concurrency: 1)
@only_ids = parse_project_ids(only_ids)
@exclude_ids = parse_project_ids(exclude_ids)
@preview = !preview.blank?
@user = user
- @success_count = 0
- @failed_projects = []
+ @success_count = Concurrent::AtomicFixnum.new(0)
+ @failed_projects = Concurrent::Array.new
@output_stream = output_stream
+ @concurrency = concurrency.to_i
end
def execute
@@ -42,39 +43,55 @@ module Ci
else
ProjectCiCdSetting.each_batch do |batch|
batch = batch.where(inbound_job_token_scope_enabled: false)
-
project_ids = batch.pluck(:project_id) - @exclude_ids # rubocop: disable Database/AvoidUsingPluckWithoutLimit -- pluck limited to batch size
- migrate_batch(project_ids)
+ chunk_size = (project_ids.size / @concurrency.to_f).ceil
+
+ next if chunk_size == 0
+
+ project_id_chunks = project_ids.each_slice(chunk_size).to_a
+
+ Parallel.each(project_id_chunks, in_threads: @concurrency) do |project_id_chunk|
+ migrate_batch(project_id_chunk)
+ end
end
end
end
def migrate_batch(project_ids)
Project.where(id: project_ids).preload(:ci_cd_settings).find_each do |project|
- @output_stream.puts migrate_project(project)
+ status, message = migrate_project(project)
+
+ if status == 'success'
+ @success_count.increment
+ else
+ @failed_projects.concat([project.id])
+ end
+
+ @output_stream.puts log_migrate_result(status, project, message)
end
end
def migrate_project(project)
- if preview_mode?
- "Would have migrated project id: #{project.id}."
- else
- result = perform_migration!(project)
+ return 'success', nil if preview_mode?
+
+ result = perform_migration!(project)
+
+ return 'success', nil if result.success?
+
+ ['error', result.message]
- if result.success?
- @success_count += 1
- "Migrated project id: #{project.id}."
- else
- log_error(project, result.message)
- end
- end
rescue StandardError => error
- log_error(project, error.message)
+ ['error', error.message]
end
- def log_error(project, message)
- @failed_projects << project
- "Error migrating project id: #{project.id}, error: #{message}"
+ def log_migrate_result(status, project, message = nil)
+ if preview_mode?
+ "Would have migrated project id: #{project.id}."
+ elsif status == 'success'
+ "Migrated project id: #{project.id}."
+ else
+ "Error migrating project id: #{project.id}, error: #{message}"
+ end
end
def perform_migration!(project)
@@ -95,9 +112,9 @@ module Ci
def start_message
if preview_mode?
- "\nMigrating project(s) in preview mode...\n\n"
+ "\nMigrating project(s) in preview mode, concurrency: #{@concurrency}...\n\n"
else
- "\nMigrating project(s)...\n\n"
+ "\nMigrating project(s), concurrency: #{@concurrency}...\n\n"
end
end
@@ -113,13 +130,14 @@ module Ci
return if preview_mode?
failure_count = @failed_projects.length
+ success_count = @success_count.value
report_lines = []
report_lines << "Summary: \n"
- report_lines << " #{@success_count} project(s) successfully migrated, #{failure_count} error(s) reported.\n"
+ report_lines << " #{success_count} project(s) successfully migrated, #{failure_count} error(s) reported.\n"
if failure_count > 0
report_lines << " The following #{failure_count} project id(s) failed to migrate:\n"
- report_lines << " #{@failed_projects.pluck(:id).join(', ')}" # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- pluck limited by array size
+ report_lines << " #{@failed_projects.join(', ')}" # -- pluck limited by array size
end
report_lines.join
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index 726feae71a9..10c3e9658d0 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -42,8 +42,11 @@ class ProjectCiCdSetting < ApplicationRecord
numericality: {
only_integer: true,
greater_than_or_equal_to: ChronicDuration.parse('1 day'),
- less_than_or_equal_to: ChronicDuration.parse('1 year'),
- message: N_('must be between 1 day and 1 year')
+ less_than_or_equal_to: ->(_) { ::Gitlab::CurrentSettings.ci_delete_pipelines_in_seconds_limit },
+ message: ->(*) {
+ format(N_('must be between 1 day and %{limit}'),
+ limit: ::Gitlab::CurrentSettings.ci_delete_pipelines_in_seconds_limit_human_readable_long)
+ }
}
attribute :forward_deployment_enabled, default: true
diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb
index e11d832b13a..b05c8094f9b 100644
--- a/app/models/project_setting.rb
+++ b/app/models/project_setting.rb
@@ -6,6 +6,7 @@ class ProjectSetting < ApplicationRecord
include CascadingProjectSettingAttribute
include Projects::SquashOption
include Gitlab::EncryptedAttribute
+ include AfterCommitQueue
ALLOWED_TARGET_PLATFORMS = %w[ios osx tvos watchos android].freeze
@@ -49,6 +50,9 @@ class ProjectSetting < ApplicationRecord
validate :pages_unique_domain_availability, if: :pages_unique_domain_changed?
+ after_update :enqueue_auto_merge_workers,
+ if: -> { Feature.enabled?(:merge_request_title_regex, project) && saved_change_to_merge_request_title_regex }
+
attribute :legacy_open_source_license_available, default: -> do
Feature.enabled?(:legacy_open_source_license_available, type: :ops)
end
@@ -96,6 +100,12 @@ class ProjectSetting < ApplicationRecord
end
end
+ def enqueue_auto_merge_workers
+ run_after_commit do
+ AutoMergeProcessWorker.perform_async({ 'project_id' => project.id })
+ end
+ end
+
def require_unique_domain?
pages_unique_domain_enabled ||
pages_unique_domain_in_database.present?
diff --git a/app/validators/json_schemas/application_setting_ci_cd_settings.json b/app/validators/json_schemas/application_setting_ci_cd_settings.json
index 2eaf126ad9b..6e7afa7e9c2 100644
--- a/app/validators/json_schemas/application_setting_ci_cd_settings.json
+++ b/app/validators/json_schemas/application_setting_ci_cd_settings.json
@@ -11,6 +11,10 @@
"ci_partitions_size_limit": {
"type": "integer",
"description": "Maximum table size limit for a CI partition used for creating new partitions."
+ },
+ "ci_delete_pipelines_in_seconds_limit": {
+ "type": "integer",
+ "description": "Maximum value allowed for configuring pipeline retention."
}
}
}
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 1dd8188180a..2208cf5ef5a 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -92,7 +92,7 @@
= form.label :delete_pipelines_in_human_readable, s_('CICD|Automatic pipeline cleanup'), class: 'label-bold'
= form.text_field :delete_pipelines_in_human_readable, { class: 'form-control gl-form-input' }
%p.form-text.gl-text-subtle
- = html_escape(s_("CICD|Pipelines older than the configured time are deleted. Leave empty to never delete pipelines automatically. The default unit is in seconds, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}1 year%{code_close}. Can be between 1 day to 1 year.")) % { code_open: ''.html_safe, code_close: '
'.html_safe }
+ = html_escape(s_("CICD|Pipelines older than the configured time are deleted. Leave empty to never delete pipelines automatically. The default unit is in seconds, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}1 year%{code_close}. Can be between 1 day to %{limit}.")) % { code_open: ''.html_safe, code_close: '
'.html_safe, limit: ::Gitlab::CurrentSettings.ci_delete_pipelines_in_seconds_limit_human_readable_long }
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings.md', anchor: 'automatic-pipeline-cleanup'), target: '_blank', rel: 'noopener noreferrer'
= f.submit _('Save changes'), pajamas_button: true
diff --git a/app/workers/auto_merge_process_worker.rb b/app/workers/auto_merge_process_worker.rb
index b26efc5f993..d2a99276e6b 100644
--- a/app/workers/auto_merge_process_worker.rb
+++ b/app/workers/auto_merge_process_worker.rb
@@ -14,6 +14,8 @@ class AutoMergeProcessWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :continuous_delivery
worker_resource_boundary :cpu
+ PROJECT_MR_LIMIT = 30
+
def perform(params = {})
# Passing an integer id to AutoMergeProcessWorker is deprecated.
# This is here to support existing implementations while we transition
@@ -30,7 +32,16 @@ class AutoMergeProcessWorker # rubocop:disable Scalability/IdempotentWorker
end
end
- all_merge_requests = merge_requests.to_a + pipeline_merge_requests.to_a
+ project_merge_requests = params['project_id'].try do |proj_id|
+ project = Project.id_in(proj_id).first
+
+ break [] unless project
+ break [] unless Feature.enabled?(:merge_request_title_regex, project)
+
+ project.merge_requests&.with_auto_merge_enabled&.take(PROJECT_MR_LIMIT)
+ end
+
+ all_merge_requests = (merge_requests.to_a + pipeline_merge_requests.to_a + project_merge_requests.to_a).uniq
all_merge_requests.each do |merge_request|
AutoMergeService.new(merge_request.project, merge_request.merge_user).process(merge_request)
diff --git a/app/workers/bulk_imports/transform_references_worker.rb b/app/workers/bulk_imports/transform_references_worker.rb
index 73c05f8496b..203eb1fbbaf 100644
--- a/app/workers/bulk_imports/transform_references_worker.rb
+++ b/app/workers/bulk_imports/transform_references_worker.rb
@@ -91,6 +91,8 @@ module BulkImports
next unless parsed_url.path&.start_with?("/#{source_full_path}")
array << [url, new_url(object, parsed_url)]
+ rescue URI::InvalidURIError
+ # Some strings like http://[127.0.0.1] are captured by URI.extract, but fail to parse. We can ignore these cases
end
end
diff --git a/config/initializers_before_autoloader/100_patch_omniauth_saml.rb b/config/initializers_before_autoloader/100_patch_omniauth_saml.rb
index d46842ed9a0..8fb313524c9 100644
--- a/config/initializers_before_autoloader/100_patch_omniauth_saml.rb
+++ b/config/initializers_before_autoloader/100_patch_omniauth_saml.rb
@@ -5,6 +5,9 @@ require 'omniauth/strategies/saml'
module OmniAuth
module Strategies
class SAML
+ # Store the original method
+ alias_method :original_callback_url, :callback_url
+
# NOTE: This method duplicates code from omniauth-saml
# so that we can access authn_request to store it
# See: https://github.com/omniauth/omniauth-saml/issues/172
@@ -18,6 +21,13 @@ module OmniAuth
end
end
+ # NOTE: Overriding the callback_url method since in certain cases
+ # IDP doesn't return the correct ACS URL for us to validate
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/491634
+ def callback_url
+ full_host + callback_path
+ end
+
private
def store_authn_request_id(authn_request)
diff --git a/db/migrate/20250423133838_add_name_and_description_to_maven_vreg_registries.rb b/db/migrate/20250423133838_add_name_and_description_to_maven_vreg_registries.rb
new file mode 100644
index 00000000000..98a40a24bf6
--- /dev/null
+++ b/db/migrate/20250423133838_add_name_and_description_to_maven_vreg_registries.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddNameAndDescriptionToMavenVregRegistries < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+ milestone '18.0'
+
+ TABLE_NAME = :virtual_registries_packages_maven_registries
+
+ def up
+ with_lock_retries do
+ add_column TABLE_NAME, :name, :text, if_not_exists: true, null: false, default: ''
+ add_column TABLE_NAME, :description, :text, if_not_exists: true
+ end
+
+ add_text_limit TABLE_NAME, :name, 255
+ add_text_limit TABLE_NAME, :description, 1024
+ end
+
+ def down
+ with_lock_retries do
+ remove_column TABLE_NAME, :name, if_exists: true
+ remove_column TABLE_NAME, :description, if_exists: true
+ end
+ end
+end
diff --git a/db/migrate/20250429095731_add_name_and_description_to_maven_vreg_upstreams.rb b/db/migrate/20250429095731_add_name_and_description_to_maven_vreg_upstreams.rb
new file mode 100644
index 00000000000..275757902f3
--- /dev/null
+++ b/db/migrate/20250429095731_add_name_and_description_to_maven_vreg_upstreams.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddNameAndDescriptionToMavenVregUpstreams < Gitlab::Database::Migration[2.3]
+ disable_ddl_transaction!
+ milestone '18.0'
+
+ TABLE_NAME = :virtual_registries_packages_maven_upstreams
+
+ def up
+ with_lock_retries do
+ add_column TABLE_NAME, :name, :text, if_not_exists: true, null: false, default: ''
+ add_column TABLE_NAME, :description, :text, if_not_exists: true
+ end
+
+ add_text_limit TABLE_NAME, :name, 255
+ add_text_limit TABLE_NAME, :description, 1024
+ end
+
+ def down
+ with_lock_retries do
+ remove_column TABLE_NAME, :name, if_exists: true
+ remove_column TABLE_NAME, :description, if_exists: true
+ end
+ end
+end
diff --git a/db/schema_migrations/20250423133838 b/db/schema_migrations/20250423133838
new file mode 100644
index 00000000000..75fdb380c09
--- /dev/null
+++ b/db/schema_migrations/20250423133838
@@ -0,0 +1 @@
+b72c49b58649b777f2a7e0456bc4d6b4ab11b674286ab9bde622bc6ee33448ff
\ No newline at end of file
diff --git a/db/schema_migrations/20250429095731 b/db/schema_migrations/20250429095731
new file mode 100644
index 00000000000..770209b3186
--- /dev/null
+++ b/db/schema_migrations/20250429095731
@@ -0,0 +1 @@
+705ff147cac5afbe80c52e3f091ec8178c9b0231c0ae07958b1aa282b2294f3c
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 9569d7557e8..084e98b9c9d 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -24496,7 +24496,11 @@ CREATE TABLE virtual_registries_packages_maven_registries (
id bigint NOT NULL,
group_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
- updated_at timestamp with time zone NOT NULL
+ updated_at timestamp with time zone NOT NULL,
+ name text DEFAULT ''::text NOT NULL,
+ description text,
+ CONSTRAINT check_1c00ac80ea CHECK ((char_length(name) <= 255)),
+ CONSTRAINT check_ee44adc558 CHECK ((char_length(description) <= 1024))
);
CREATE SEQUENCE virtual_registries_packages_maven_registries_id_seq
@@ -24537,9 +24541,13 @@ CREATE TABLE virtual_registries_packages_maven_upstreams (
cache_validity_hours smallint DEFAULT 24 NOT NULL,
username jsonb,
password jsonb,
+ name text DEFAULT ''::text NOT NULL,
+ description text,
CONSTRAINT check_26c0572777 CHECK ((char_length(url) <= 255)),
CONSTRAINT check_4db365ecc9 CHECK (((num_nonnulls(username, password) = 2) OR (num_nulls(username, password) = 2))),
- CONSTRAINT check_a3593dca3a CHECK ((cache_validity_hours >= 0))
+ CONSTRAINT check_a3593dca3a CHECK ((cache_validity_hours >= 0)),
+ CONSTRAINT check_c827be970e CHECK ((char_length(description) <= 1024)),
+ CONSTRAINT check_f92d4b3613 CHECK ((char_length(name) <= 255))
);
CREATE SEQUENCE virtual_registries_packages_maven_upstreams_id_seq
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 047495da5db..0399aebf4b4 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -987,6 +987,25 @@ To change the limit, update `ci_partitions_size_limit` with the new value. For e
ApplicationSetting.update(ci_partitions_size_limit: 20.gigabytes)
```
+### Maximum config value for automatic pipeline cleanup
+
+{{< history >}}
+
+- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189191) in GitLab 18.0.
+
+{{< /history >}}
+
+Configures the upper limit for [CI/CD pipeline expiry time](../ci/pipelines/settings.md#automatic-pipeline-cleanup).
+Defaults to 1 year.
+
+You can change this limit by using the [GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session).
+To change the limit, update `ci_delete_pipelines_in_seconds_limit_human_readable` with the new value.
+For example, to set it to 3 years:
+
+```ruby
+ApplicationSetting.update(ci_delete_pipelines_in_seconds_limit_human_readable: '3 years')
+```
+
## Instance monitoring and metrics
### Limit inbound incident management alerts
diff --git a/doc/api/openapi/openapi_v2.yaml b/doc/api/openapi/openapi_v2.yaml
index 3c852f88915..fa4add30c6c 100644
--- a/doc/api/openapi/openapi_v2.yaml
+++ b/doc/api/openapi/openapi_v2.yaml
@@ -15023,7 +15023,7 @@ paths:
'200':
description: List recipe files
schema:
- "$ref": "#/definitions/API_Entities_Packages_Conan_RecipeFilesList"
+ "$ref": "#/definitions/API_Entities_Packages_Conan_FilesList"
'400':
description: Bad Request
'401':
@@ -15314,6 +15314,76 @@ paths:
tags:
- conan_packages
operationId: getApiV4ProjectsIdPackagesConanV2ConansPackageNamePackageVersionPackageUsernamePackageChannelRevisionsRecipeRevisionPackagesConanPackageReferenceLatest
+ ? "/api/v4/projects/{id}/packages/conan/v2/conans/{package_name}/{package_version}/{package_username}/{package_channel}/revisions/{recipe_revision}/packages/{conan_package_reference}/revisions/{package_revision}/files"
+ : get:
+ summary: List package files
+ description: This feature was introduced in GitLab 18.0
+ produces:
+ - application/json
+ parameters:
+ - in: path
+ name: id
+ description: The ID or URL-encoded path of the project
+ type: string
+ required: true
+ - in: path
+ name: package_name
+ description: Package name
+ type: string
+ required: true
+ example: my-package
+ - in: path
+ name: package_version
+ description: Package version
+ type: string
+ required: true
+ example: '1.0'
+ - in: path
+ name: package_username
+ description: Package username
+ type: string
+ required: true
+ example: my-group+my-project
+ - in: path
+ name: package_channel
+ description: Package channel
+ type: string
+ required: true
+ example: stable
+ - in: path
+ name: recipe_revision
+ description: Recipe revision
+ type: string
+ required: true
+ example: df28fd816be3a119de5ce4d374436b25
+ - in: path
+ name: conan_package_reference
+ description: Package reference
+ type: string
+ required: true
+ example: 5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
+ - in: path
+ name: package_revision
+ description: Package revision
+ type: string
+ required: true
+ example: 3bdd2d8c8e76c876ebd1ac0469a4e72c
+ responses:
+ '200':
+ description: List package files
+ schema:
+ "$ref": "#/definitions/API_Entities_Packages_Conan_FilesList"
+ '400':
+ description: Bad Request
+ '401':
+ description: Unauthorized
+ '403':
+ description: Forbidden
+ '404':
+ description: Not Found
+ tags:
+ - conan_packages
+ operationId: getApiV4ProjectsIdPackagesConanV2ConansPackageNamePackageVersionPackageUsernamePackageChannelRevisionsRecipeRevisionPackagesConanPackageReferenceRevisionsPackageRevisionFiles
? "/api/v4/projects/{id}/packages/conan/v2/conans/{package_name}/{package_version}/{package_username}/{package_channel}/revisions/{recipe_revision}/packages/{conan_package_reference}/revisions/{package_revision}/files/{file_name}"
: get:
summary: Download package files
@@ -51358,14 +51428,14 @@ definitions:
"$ref": "#/definitions/API_Entities_Packages_Conan_Revision"
description: List of recipe revisions
description: API_Entities_Packages_Conan_RecipeRevisions model
- API_Entities_Packages_Conan_RecipeFilesList:
+ API_Entities_Packages_Conan_FilesList:
type: object
properties:
files:
type: object
example: '{ "files" : { "conan_sources.tgz" : { }, "conanmanifest.txt" : {
}, "conanfile.py" : { } } }'
- description: API_Entities_Packages_Conan_RecipeFilesList model
+ description: API_Entities_Packages_Conan_FilesList model
? putApiV4ProjectsIdPackagesConanV2ConansPackageNamePackageVersionPackageUsernamePackageChannelRevisionsRecipeRevisionFilesFileName
: type: object
properties:
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 122efa574d3..cfb0b2b2f65 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -465,6 +465,7 @@ to configure other related settings. These requirements are
| `bulk_import_max_download_file_size` | integer | no | Maximum download file size when importing from source GitLab instances by direct transfer. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384976) in GitLab 16.3. |
| `can_create_group` | boolean | no | Indicates whether users can create top-level groups. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367754) in GitLab 15.5. Defaults to `true`. |
| `check_namespace_plan` | boolean | no | Enabling this makes only licensed EE features available to projects if the project namespace's plan includes the feature or if the project is public. Premium and Ultimate only. |
+| `ci_delete_pipelines_in_seconds_limit_human_readable` | string | no | Maximum value that is allowed for configuring pipeline retention. Defaults to `1 year`. |
| `ci_job_live_trace_enabled` | boolean | no | Turns on incremental logging for job logs. When turned on, archived job logs are incrementally uploaded to object storage. Object storage must be configured. You can also configure this setting in the [**Admin** area](../administration/settings/continuous_integration.md#incremental-logging). |
| `ci_max_total_yaml_size_bytes` | integer | no | The maximum amount of memory, in bytes, that can be allocated for the pipeline configuration, with all included YAML configuration files. |
| `ci_max_includes` | integer | no | The [maximum number of includes](../administration/settings/continuous_integration.md#set-maximum-includes) per pipeline. Default is `150`. |
diff --git a/doc/ci/jobs/fine_grained_permissions.md b/doc/ci/jobs/fine_grained_permissions.md
index 3342f4e39c1..52bd075c7e7 100644
--- a/doc/ci/jobs/fine_grained_permissions.md
+++ b/doc/ci/jobs/fine_grained_permissions.md
@@ -191,6 +191,7 @@ The following endpoints are available for CI/CD job tokens.
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/files` | List recipe files |
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/latest` | Get the latest package revision |
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files/:file_name` | Download package files |
+| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions/:recipe_revision/packages/:conan_package_reference/revisions/:package_revision/files` | List package files |
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/conan/v2/conans/:package_name/:package_version/:package_username/:package_channel/revisions` | Get the list of revisions |
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/generic/:package_name/*package_version/(*path/):file_name` | Download package file |
| Packages: Read | `READ_PACKAGES` | `GET /projects/:id/packages/go/*module_name/@v/:module_version.info` | Version metadata |
diff --git a/doc/development/documentation/testing/_index.md b/doc/development/documentation/testing/_index.md
index 66f5d62a9bd..ccad1e068c4 100644
--- a/doc/development/documentation/testing/_index.md
+++ b/doc/development/documentation/testing/_index.md
@@ -333,3 +333,4 @@ We also run some documentation tests in these projects:
- GitLab Extension for Visual Studio:
- AI gateway:
- Prompt Library:
+- GitLab Container Registry:
diff --git a/doc/user/gitlab_duo/turn_on_off.md b/doc/user/gitlab_duo/turn_on_off.md
index c8080ac7aae..0f6fe624aee 100644
--- a/doc/user/gitlab_duo/turn_on_off.md
+++ b/doc/user/gitlab_duo/turn_on_off.md
@@ -7,8 +7,8 @@ title: Control GitLab Duo availability
{{< history >}}
-- [Settings to turn off AI features introduced](https://gitlab.com/groups/gitlab-org/-/epics/12404) in GitLab 16.10.
-- [Settings to turn off AI features added to the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/441489) in GitLab 16.11.
+- [Settings to turn AI features on and off introduced](https://gitlab.com/groups/gitlab-org/-/epics/12404) in GitLab 16.10.
+- [Settings to turn AI features on and off added to the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/441489) in GitLab 16.11.
{{< /history >}}
@@ -25,9 +25,9 @@ To turn on GitLab Duo Self-Hosted, see [Configure GitLab to access GitLab Duo Se
{{< /alert >}}
-## Turn off GitLab Duo features
+## Turn GitLab Duo features on or off
-You can turn off GitLab Duo for a group, project, or instance.
+You can turn GitLab Duo on or off for a group, project, or instance.
When GitLab Duo is turned off for a group, project, or instance:
@@ -35,36 +35,34 @@ When GitLab Duo is turned off for a group, project, or instance:
- Code Suggestions is not available.
- GitLab Duo Chat is not available.
-### Turn off for a group or subgroup
+### For a group or subgroup
{{< tabs >}}
{{< tab title="In 17.8 and later" >}}
-In GitLab 17.8 and later, follow these instructions to turn off GitLab Duo
+In GitLab 17.8 and later, follow these instructions to turn GitLab Duo on or off
for a group, including its subgroups and projects.
Prerequisites:
- You must have the Owner role for the group.
-To turn off GitLab Duo for a group or subgroup:
+To turn GitLab Duo on or off for a group or subgroup:
1. On the left sidebar, select **Search or go to** and find your group or subgroup.
1. Go to the settings, based on your deployment type and group level:
- For GitLab.com top-level groups: Select **Settings > GitLab Duo** and select **Change configuration**.
- For GitLab.com subgroups: Select **Settings > General** and expand **GitLab Duo features**.
- For GitLab Self-Managed (all groups and subgroups): Select **Settings > General** and expand **GitLab Duo features**.
-1. Choose an option:
- - To turn off GitLab Duo for the group, but let other groups or projects turn it on, select **Off by default**.
- - To turn off GitLab Duo for the group, and to prevent other groups or projects from turning it on, select **Always off**.
+1. Choose an option.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.7" >}}
-In GitLab 17.7, follow these instructions to turn off GitLab Duo
+In GitLab 17.7, follow these instructions to turn GitLab Duo on or off
for a group, including its subgroups and projects.
{{< alert type="note" >}}
@@ -81,21 +79,19 @@ Prerequisites:
- You must have the Owner role for the group.
-To turn off GitLab Duo for a top-level group:
+To turn GitLab Duo on or off for a top-level group:
1. On the left sidebar, select **Search or go to** and find your top-level group.
1. Select **Settings > GitLab Duo**.
1. Select **Change configuration**.
-1. Choose an option:
- - To turn off GitLab Duo for the group, but let other projects turn it on, select **Off by default**.
- - To turn off GitLab Duo for the group, and to prevent projects from turning it on, select **Always off**.
+1. Choose an option.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.4 to 17.6" >}}
-In GitLab 17.4 to 17.6, follow these instructions to turn off GitLab Duo
+In GitLab 17.4 to 17.6, follow these instructions to turn GitLab Duo on or off
for a group and its subgroups and projects.
{{< alert type="note" >}}
@@ -112,33 +108,31 @@ Prerequisites:
- You must have the Owner role for the group.
-To turn off GitLab Duo for a top-level group:
+To turn GitLab Duo on or off for a top-level group:
1. On the left sidebar, select **Search or go to** and find your top-level group.
1. Select **Settings > GitLab Duo**.
1. Select **Change configuration**.
-1. Choose an option:
- - To turn off GitLab Duo for the group, but let other groups or projects turn it on, select **Off by default**.
- - To turn off GitLab Duo for the group, and to prevent other groups or projects from turning it on, select **Never on**.
+1. Choose an option.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.3 and earlier" >}}
-In GitLab 17.3 and earlier, follow these instructions to turn off GitLab Duo for a group
+In GitLab 17.3 and earlier, follow these instructions to turn GitLab Duo on or off for a group
and its subgroups and projects.
Prerequisites:
- You must have the Owner role for the group.
-To turn off GitLab Duo for a group or subgroup:
+To turn GitLab Duo on or off for a group or subgroup:
1. On the left sidebar, select **Search or go to** and find your group or subgroup.
1. Select **Settings > General**.
1. Expand **Permissions and group features**.
-1. Clear the **Use GitLab Duo features** checkbox.
+1. Select or clear the **Use GitLab Duo features** checkbox.
1. Optional. Select the **Enforce for all subgroups** checkbox to cascade the setting to
all subgroups.
@@ -148,44 +142,44 @@ To turn off GitLab Duo for a group or subgroup:
{{< /tabs >}}
-### Turn off for a project
+### For a project
{{< tabs >}}
{{< tab title="In 17.4 and later" >}}
-In GitLab 17.4 and later, follow these instructions to turn off GitLab Duo for a project.
+In GitLab 17.4 and later, follow these instructions to turn GitLab Duo on or off for a project.
Prerequisites:
- You must have the Owner role for the project.
-To turn off GitLab Duo for a project:
+To turn GitLab Duo on or off for a project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
-1. Under **GitLab Duo**, turn the toggle off.
+1. Under **GitLab Duo**, turn the toggle on or off.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.3 and earlier" >}}
-In GitLab 17.3 and earlier, follow these instructions to turn off GitLab Duo for a project.
+In GitLab 17.3 and earlier, follow these instructions to turn GitLab Duo on or off for a project.
1. Use the GitLab GraphQL API
[`projectSettingsUpdate`](../../api/graphql/reference/_index.md#mutationprojectsettingsupdate)
mutation.
1. Set the
[`duo_features_enabled`](../../api/graphql/getting_started.md#update-project-settings)
- setting to `false`. (The default is `true`.)
+ setting to `true` or `false`.
{{< /tab >}}
{{< /tabs >}}
-### Turn off for an instance
+### For an instance
{{< details >}}
@@ -197,58 +191,54 @@ In GitLab 17.3 and earlier, follow these instructions to turn off GitLab Duo for
{{< tab title="In 17.7 and later" >}}
-In GitLab 17.7 and later, follow these instructions to turn off GitLab Duo for the instance.
+In GitLab 17.7 and later, follow these instructions to turn GitLab Duo on or off for an instance.
Prerequisites:
- You must be an administrator.
-To turn off GitLab Duo for an instance:
+To turn GitLab Duo on or off for an instance:
1. On the left sidebar, at the bottom, select **Admin area**.
1. Select **GitLab Duo**.
1. Select **Change configuration**.
-1. Choose an option:
- - To turn off GitLab Duo for the instance, but let groups and projects turn it on, select **Off by default**.
- - To turn off GitLab Duo for the instance, and to prevent groups or projects from ever turning it on, select **Always off**.
+1. Choose an option.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.4 to 17.6" >}}
-In GitLab 17.4 to 17.6, follow these instructions to turn off GitLab Duo for the instance.
+In GitLab 17.4 to 17.6, follow these instructions to turn GitLab Duo on or off for the instance.
Prerequisites:
- You must be an administrator.
-To turn off GitLab Duo for an instance:
+To turn GitLab Duo on or off for an instance:
1. On the left sidebar, at the bottom, select **Admin area**.
1. Select **Settings > General**.
1. Expand **GitLab Duo features**.
-1. Choose an option:
- - To turn off GitLab Duo for the instance, but let groups and projects turn it on, select **Off by default**.
- - To turn off GitLab Duo for the instance, and to prevent groups or projects from ever turning it on, select **Never on**.
+1. Choose an option.
1. Select **Save changes**.
{{< /tab >}}
{{< tab title="In 17.3 and earlier" >}}
-In GitLab 17.3 and earlier, follow these instructions to turn off GitLab Duo for an instance.
+In GitLab 17.3 and earlier, follow these instructions to turn GitLab Duo on or off for an instance.
Prerequisites:
- You must be an administrator.
-To turn off GitLab Duo for an instance:
+To turn GitLab Duo on or off for an instance:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > General**.
1. Expand **AI-powered features**.
-1. Clear the **Use Duo features** checkbox.
+1. Select or clear the **Use Duo features** checkbox.
1. Optional. Select the **Enforce for all subgroups** checkbox to cascade
the setting to all groups in the instance.
@@ -323,7 +313,7 @@ turn on GitLab Duo experimental and beta features for your group on GitLab.com.
This setting [cascades to all projects](../project/merge_requests/approvals/settings.md#cascade-settings-from-the-instance-or-top-level-group)
that belong to the group.
-### On self-managed
+### On GitLab Self-Managed
{{< tabs >}}
diff --git a/doc/user/gitlab_duo_chat/turn_on_off.md b/doc/user/gitlab_duo_chat/turn_on_off.md
index dc1e33f649c..0c7884e49dd 100644
--- a/doc/user/gitlab_duo_chat/turn_on_off.md
+++ b/doc/user/gitlab_duo_chat/turn_on_off.md
@@ -14,7 +14,7 @@ In GitLab 16.11 and later, GitLab Duo Chat is:
- Generally available.
- Available to any user with an assigned GitLab Duo seat.
-If you [turn on or turn off GitLab Duo](../gitlab_duo/turn_on_off.md), you turn on or turn off Duo Chat as well.
+If you [turn GitLab Duo on or off](../gitlab_duo/turn_on_off.md), you turn on or turn off Duo Chat as well.
## For GitLab Self-Managed
@@ -78,14 +78,14 @@ automatically enabled for those with GitLab Duo Pro or Enterprise.
In GitLab 16.8, 16.9, and 16.10, on GitLab Dedicated, GitLab Duo Chat is available in beta.
-## Disable GitLab Duo Chat
+## Turn off GitLab Duo Chat
To limit the data that GitLab Duo Chat has access to, follow the instructions for
-[disabling GitLab Duo features](../gitlab_duo/turn_on_off.md#turn-off-gitlab-duo-features).
+[turning off GitLab Duo features](../gitlab_duo/turn_on_off.md#turn-gitlab-duo-features-on-or-off).
-## Disable Chat in VS Code
+## Turn off Chat in VS Code
-To disable GitLab Duo Chat in VS Code:
+To turn off GitLab Duo Chat in VS Code:
1. Go to **Settings > Extensions > GitLab Workflow**.
1. Clear the **Enable GitLab Duo Chat assistant** checkbox.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 30d489c3ef1..a89cad21d0f 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -249,7 +249,7 @@ Project permissions for [issues](project/issues/_index.md):
| Move [test cases](../ci/test_cases/_index.md) | | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Reopen [test cases](../ci/test_cases/_index.md) | | ✓ | ✓ | ✓ | ✓ | ✓ | |
| [Import](project/issues/csv_import.md) issues from a CSV file | | ✓ | | ✓ | ✓ | ✓ | |
-| [Export](project/issues/csv_export.md) issues to a CSV file | | ✓ | ✓ | ✓ | ✓ | ✓ | |
+| [Export](project/issues/csv_export.md) issues to a CSV file | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Delete issues | | ✓ | | | | ✓ | |
| Manage [Feature flags](../operations/feature_flags.md) | | | | ✓ | ✓ | ✓ | |
@@ -413,7 +413,7 @@ Project permissions for [GitLab Duo](gitlab_duo/_index.md):
| Action | Non-member | Guest | Planner | Reporter | Developer | Maintainer | Owner | Notes |
| -------------------------------------------------------------------------------------- | ---------- | ----- | ------- | -------- | --------- | ---------- | ----- | ----- |
| Use Duo features | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Code Suggestions requires a [user being assigned a seat to gain access to a Duo add-on](../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats). |
-| Configure [Duo feature availability](gitlab_duo/turn_on_off.md#turn-off-for-a-project) | | | | | | ✓ | ✓ | |
+| Configure [Duo feature availability](gitlab_duo/turn_on_off.md#for-a-project) | | | | | | ✓ | ✓ | |
## Group members permissions
@@ -482,7 +482,7 @@ Group permissions for [GitLab Duo](gitlab_duo/_index.md):
| Action | Non-member | Guest | Planner | Reporter | Developer | Maintainer | Owner | Notes |
| --------------------------------------------------------------------------------------------------------- | :--------: | :---: | :-----: | :------: | :-------: | :--------: | :---: | ----- |
| Use Duo features | | | | ✓ | ✓ | ✓ | ✓ | Requires [user being assigned a seat to gain access to a Duo add-on](../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats). |
-| Configure [Duo feature availability](gitlab_duo/turn_on_off.md#turn-off-for-a-group-or-subgroup) | | | | | | ✓ | ✓ | |
+| Configure [Duo feature availability](gitlab_duo/turn_on_off.md#for-a-group-or-subgroup) | | | | | | ✓ | ✓ | |
| Configure [GitLab Duo Self Hosted](../administration/gitlab_duo_self_hosted/configure_duo_features.md) | | | | | | | ✓ | |
| Enable [beta and experimental features](gitlab_duo/turn_on_off.md#turn-on-beta-and-experimental-features) | | | | | | | ✓ | |
| Purchase [Duo seats](../subscriptions/subscription-add-ons.md#purchase-additional-gitlab-duo-seats) | | | | | | | ✓ | |
diff --git a/doc/user/project/issues/csv_export.md b/doc/user/project/issues/csv_export.md
index c6d233c6a53..c6cc05926e3 100644
--- a/doc/user/project/issues/csv_export.md
+++ b/doc/user/project/issues/csv_export.md
@@ -12,12 +12,6 @@ title: Export issues to CSV
{{< /details >}}
-{{< history >}}
-
-- Minimum role to export issues [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/169256) from Reporter to Planner in GitLab 17.7.
-
-{{< /history >}}
-
You can export issues from GitLab to a plain-text CSV
([comma-separated values](https://en.wikipedia.org/wiki/Comma-separated_values))
file. The CSV file is attached to an email, and sent to your default
@@ -45,7 +39,7 @@ You can export issues from individual projects, but not groups.
Prerequisites:
-- You must have at least the Planner role.
+- You must have at least the Guest role.
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Plan > Issues**.
diff --git a/doc/user/project/repository/code_suggestions/set_up.md b/doc/user/project/repository/code_suggestions/set_up.md
index 606b9e57adf..675aa767c6f 100644
--- a/doc/user/project/repository/code_suggestions/set_up.md
+++ b/doc/user/project/repository/code_suggestions/set_up.md
@@ -175,4 +175,4 @@ For more information, see the [JetBrains product documentation](https://www.jetb
### Turn off GitLab Duo
-Alternatively, you can [turn off GitLab Duo](../../../gitlab_duo/turn_on_off.md#turn-off-gitlab-duo-features) (which includes Code Suggestions) completely for a group, project, or instance.
+Alternatively, you can [turn off GitLab Duo](../../../gitlab_duo/turn_on_off.md#turn-gitlab-duo-features-on-or-off) (which includes Code Suggestions) completely for a group, project, or instance.
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index cedfadf6e6a..a187e0d9ecd 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -543,8 +543,7 @@ To view only the projects you are the owner of:
## Rename a repository
-A project's repository name defines its URL and its place on the file disk
-where GitLab is installed.
+A project's repository name defines its URL.
Prerequisites:
diff --git a/doc/user/search/advanced_search.md b/doc/user/search/advanced_search.md
index afc05d6db94..521ff213944 100644
--- a/doc/user/search/advanced_search.md
+++ b/doc/user/search/advanced_search.md
@@ -91,11 +91,11 @@ You can refine user search with `simple_query_string`.
## Known issues
-- You can only search files smaller than 1 MB.
+- You can search only files smaller than 1 MB.
For more information, see [issue 195764](https://gitlab.com/gitlab-org/gitlab/-/issues/195764).
For GitLab Self-Managed, an administrator can
[configure the **Maximum file size indexed** setting](../../integration/advanced_search/elasticsearch.md#advanced-search-configuration).
-- You can use advanced search on the default branch of a project only.
+- You can use advanced search only on the default branch of a project.
For more information, see [issue 229966](https://gitlab.com/gitlab-org/gitlab/-/issues/229966).
- The search query must not contain any of the following characters:
diff --git a/doc/user/search/exact_code_search.md b/doc/user/search/exact_code_search.md
index af5103d6509..95f279a15a2 100644
--- a/doc/user/search/exact_code_search.md
+++ b/doc/user/search/exact_code_search.md
@@ -25,6 +25,8 @@ title: Exact code search
This feature is in [beta](../../policy/development_stages_support.md#beta) and subject to change without notice.
For more information, see [epic 9404](https://gitlab.com/groups/gitlab-org/-/epics/9404).
+To provide feedback on this feature, leave a comment on
+[issue 420920](https://gitlab.com/gitlab-org/gitlab/-/issues/420920).
{{< /alert >}}
@@ -132,9 +134,9 @@ This table shows some example queries for exact match and regular expression mod
## Known issues
-- You can only search files smaller than 1 MB with less than `20_000` trigrams.
+- You can search only files smaller than 1 MB with less than `20_000` trigrams.
For more information, see [issue 455073](https://gitlab.com/gitlab-org/gitlab/-/issues/455073).
-- You can use exact code search on the default branch of a project only.
+- You can use exact code search only on the default branch of a project.
For more information, see [issue 403307](https://gitlab.com/gitlab-org/gitlab/-/issues/403307).
- Multiple matches on a single line are counted as one result.
For more information, see [issue 514526](https://gitlab.com/gitlab-org/gitlab/-/issues/514526).
diff --git a/lib/api/conan/v2/project_packages.rb b/lib/api/conan/v2/project_packages.rb
index 668f46d1983..7f690657b6a 100644
--- a/lib/api/conan/v2/project_packages.rb
+++ b/lib/api/conan/v2/project_packages.rb
@@ -12,6 +12,16 @@ module API
end
end
+ helpers do
+ def package_files(finder_params)
+ ::Packages::Conan::PackageFilesFinder
+ .new(package, **finder_params)
+ .execute
+ .limit(MAX_FILES_COUNT)
+ .select(:file_name)
+ end
+ end
+
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
@@ -90,7 +100,7 @@ module API
namespace 'files' do
desc 'List recipe files' do
detail 'This feature was introduced in GitLab 17.11'
- success code: 200, model: ::API::Entities::Packages::Conan::RecipeFilesList
+ success code: 200, model: ::API::Entities::Packages::Conan::FilesList
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
@@ -105,15 +115,10 @@ module API
get urgency: :low do
not_found!('Package') unless package
- files = ::Packages::Conan::PackageFilesFinder
- .new(package, conan_file_type: :recipe_file, recipe_revision: params[:recipe_revision])
- .execute
- .limit(MAX_FILES_COUNT)
- .select(:file_name)
-
+ files = package_files(conan_file_type: :recipe_file, recipe_revision: params[:recipe_revision])
not_found!('Recipe files') if files.empty?
- present({ files: }, with: ::API::Entities::Packages::Conan::RecipeFilesList)
+ present({ files: }, with: ::API::Entities::Packages::Conan::FilesList)
end
params do
@@ -225,6 +230,33 @@ module API
end
namespace ':package_revision' do
namespace 'files' do
+ desc 'List package files' do
+ detail 'This feature was introduced in GitLab 18.0'
+ success code: 200, model: ::API::Entities::Packages::Conan::FilesList
+ failure [
+ { code: 400, message: 'Bad Request' },
+ { code: 401, message: 'Unauthorized' },
+ { code: 403, message: 'Forbidden' },
+ { code: 404, message: 'Not Found' }
+ ]
+ tags %w[conan_packages]
+ end
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
+ route_setting :authorization, job_token_policies: :read_packages,
+ allow_public_access_for_enabled_project_features: :package_registry
+ get urgency: :low do
+ not_found!('Package') unless package
+
+ files = package_files(conan_file_type: :package_file,
+ recipe_revision: params[:recipe_revision],
+ conan_package_reference: params[:conan_package_reference],
+ package_revision: params[:package_revision])
+
+ not_found!('Package files') if files.empty?
+
+ present({ files: }, with: ::API::Entities::Packages::Conan::FilesList)
+ end
+
params do
requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES,
documentation: { example: 'conaninfo.txt' }
diff --git a/lib/api/entities/packages/conan/recipe_files_list.rb b/lib/api/entities/packages/conan/files_list.rb
similarity index 90%
rename from lib/api/entities/packages/conan/recipe_files_list.rb
rename to lib/api/entities/packages/conan/files_list.rb
index 9dc6ce9856a..01a47ee98dc 100644
--- a/lib/api/entities/packages/conan/recipe_files_list.rb
+++ b/lib/api/entities/packages/conan/files_list.rb
@@ -4,7 +4,7 @@ module API
module Entities
module Packages
module Conan
- class RecipeFilesList < Grape::Entity
+ class FilesList < Grape::Entity
expose :files, documentation: {
type: 'object',
example: '{ "files" : { "conan_sources.tgz" : { }, "conanmanifest.txt" : { }, "conanfile.py" : { } } }'
diff --git a/lib/gitlab/tracking/event_eligibility_checker.rb b/lib/gitlab/tracking/event_eligibility_checker.rb
index 510b95bbd8c..61b38dbc7f5 100644
--- a/lib/gitlab/tracking/event_eligibility_checker.rb
+++ b/lib/gitlab/tracking/event_eligibility_checker.rb
@@ -3,35 +3,9 @@
module Gitlab
module Tracking
class EventEligibilityChecker
- EXTERNAL_DUO_EVENTS = {
- 'gitlab_ide_extension' => %w[
- click_button
- message_sent
- open_quick_chat
- shortcut
- suggestion_accepted
- suggestion_cancelled
- suggestion_error
- suggestion_loaded
- suggestion_not_provided
- suggestion_rejected
- suggestion_request_rejected
- suggestion_requested
- suggestion_shown
- suggestion_stream_completed
- suggestion_stream_started
- ]
- }.freeze
-
- INTERNAL_DUO_EVENTS = begin
- Gitlab::Tracking::EventDefinition.definitions.filter_map do |definition|
- definition.action if definition.duo_event?
- end.to_set
- end.freeze
-
- def eligible?(event, app_id = nil)
+ def eligible?(_event, _app_id = nil)
if ::Feature.enabled?(:collect_product_usage_events, :instance)
- snowplow_enabled? || send_usage_data? || duo_event?(event, app_id)
+ snowplow_enabled? || send_usage_data?
else
snowplow_enabled?
end
@@ -46,22 +20,8 @@ module Gitlab
def send_usage_data?
Gitlab::CurrentSettings.gitlab_product_usage_data_enabled?
end
-
- def duo_event?(event_name, app_id)
- if external_service?(app_id)
- external_duo_event?(event_name, app_id)
- else
- INTERNAL_DUO_EVENTS.include?(event_name)
- end
- end
-
- def external_service?(app_id)
- EXTERNAL_DUO_EVENTS.has_key?(app_id)
- end
-
- def external_duo_event?(event_name, app_id)
- EXTERNAL_DUO_EVENTS[app_id]&.include?(event_name)
- end
end
end
end
+
+Gitlab::Tracking::EventEligibilityChecker.prepend_mod
diff --git a/lib/tasks/ci/allowlist_migration.rake b/lib/tasks/ci/allowlist_migration.rake
index fec6bd88f85..5bd7ad54009 100644
--- a/lib/tasks/ci/allowlist_migration.rake
+++ b/lib/tasks/ci/allowlist_migration.rake
@@ -8,13 +8,15 @@ namespace :ci do
only_ids = ENV['ONLY_PROJECT_IDS']
exclude_ids = ENV['EXCLUDE_PROJECT_IDS']
preview = ENV['PREVIEW']
+ concurrency = ENV['CONCURRENCY'] || 1
require_relative '../../../app/models/ci/job_token/allowlist_migration_task'
task = ::Ci::JobToken::AllowlistMigrationTask.new(only_ids: only_ids,
exclude_ids: exclude_ids,
preview: preview,
- user: ::Users::Internal.admin_bot)
+ user: ::Users::Internal.admin_bot,
+ concurrency: concurrency.to_i)
task.execute
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d6d3c8b7718..e2bb39b5fa3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11758,7 +11758,7 @@ msgstr ""
msgid "CICD|Pipelines and jobs cannot be cancelled"
msgstr ""
-msgid "CICD|Pipelines older than the configured time are deleted. Leave empty to never delete pipelines automatically. The default unit is in seconds, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}1 year%{code_close}. Can be between 1 day to 1 year."
+msgid "CICD|Pipelines older than the configured time are deleted. Leave empty to never delete pipelines automatically. The default unit is in seconds, but you can use other units, for example %{code_open}15 days%{code_close}, %{code_open}1 month%{code_close}, %{code_open}1 year%{code_close}. Can be between 1 day to %{limit}."
msgstr ""
msgid "CICD|Pipelines triggered by composite identities, including AI agents, run automatically without review or manual interaction."
@@ -16984,6 +16984,9 @@ msgstr ""
msgid "ContainerRegistry|Add rule"
msgstr ""
+msgid "ContainerRegistry|Apply immutability rule to tags matching"
+msgstr ""
+
msgid "ContainerRegistry|Are you sure you want to delete the protected container tags rule %{tagNamePattern}?"
msgstr ""
@@ -17047,6 +17050,12 @@ msgstr ""
msgid "ContainerRegistry|Container Scanning for Registry: On"
msgstr ""
+msgid "ContainerRegistry|Container image tags can be created, overwritten, or deleted by specific user roles."
+msgstr ""
+
+msgid "ContainerRegistry|Container image tags can never be overwritten or deleted."
+msgstr ""
+
msgid "ContainerRegistry|Container protection rule created."
msgstr ""
@@ -17170,6 +17179,9 @@ msgstr ""
msgid "ContainerRegistry|Image tags"
msgstr ""
+msgid "ContainerRegistry|Immutable"
+msgstr ""
+
msgid "ContainerRegistry|Includes both tagged and untagged images"
msgstr ""
@@ -17266,12 +17278,18 @@ msgstr ""
msgid "ContainerRegistry|Protect container tags matching"
msgstr ""
+msgid "ContainerRegistry|Protected"
+msgstr ""
+
msgid "ContainerRegistry|Protected container image tags"
msgstr ""
msgid "ContainerRegistry|Protected container repositories"
msgstr ""
+msgid "ContainerRegistry|Protection type"
+msgstr ""
+
msgid "ContainerRegistry|Published %{timeInfo}"
msgstr ""
@@ -17322,6 +17340,9 @@ msgstr ""
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
+msgid "ContainerRegistry|Set up rules to protect container image tags from unauthorized changes or make them permanently immutable. Protection rules are checked first, followed by immutable rules. You can add up to 5 protection rules per project."
+msgstr ""
+
msgid "ContainerRegistry|Show full path"
msgstr ""
@@ -17382,6 +17403,9 @@ msgstr ""
msgid "ContainerRegistry|Tags that match %{strongStart}any of%{strongEnd} these rules are %{strongStart}kept%{strongEnd}, even if they match %{strongStart}all%{strongEnd} removal rules below. The %{strongStart}latest%{strongEnd} tag is always kept."
msgstr ""
+msgid "ContainerRegistry|Tags with names that match this regex pattern are immutable. Must be less than 100 characters. %{linkStart}What regex patterns are supported?%{linkEnd}"
+msgstr ""
+
msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}View regex examples.%{linkEnd}"
msgstr ""
@@ -17490,6 +17514,9 @@ msgstr ""
msgid "ContainerRegistry|index"
msgstr ""
+msgid "ContainerRegistry|protected"
+msgstr ""
+
msgid "Containers"
msgstr ""
@@ -72737,7 +72764,7 @@ msgstr ""
msgid "must be before %{expiry_date}"
msgstr ""
-msgid "must be between 1 day and 1 year"
+msgid "must be between 1 day and %{limit}"
msgstr ""
msgid "must be enabled."
diff --git a/spec/frontend/analytics/ci_cd/mock_data.js b/spec/frontend/analytics/ci_cd/mock_data.js
index 787a6032f61..5bacefd85dd 100644
--- a/spec/frontend/analytics/ci_cd/mock_data.js
+++ b/spec/frontend/analytics/ci_cd/mock_data.js
@@ -186,126 +186,6 @@ export const mockPipelineStatistics = {
},
};
-export const pipelineAnalyticsEmptyData = {
- data: {
- project: {
- id: 'gid://gitlab/Project/92',
- pipelineAnalytics: {
- aggregate: {
- count: '0',
- successCount: '0',
- failedCount: '0',
- durationStatistics: {
- p50: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- timeSeries: [
- {
- label: '2022-12-31',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-01',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-02',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-03',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-04',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-05',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-06',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- {
- label: '2023-01-07',
- successCount: '0',
- failedCount: '0',
- otherCount: '0',
- durationStatistics: {
- p50: 0,
- p95: 0,
- __typename: 'CiDurationStatistics',
- },
- __typename: 'PipelineAnalyticsPeriod',
- },
- ],
- __typename: 'PipelineAnalytics',
- },
- __typename: 'Project',
- },
- },
-};
-
export const pipelineAnalyticsData = {
data: {
project: {
diff --git a/spec/frontend/fixtures/container_registry.rb b/spec/frontend/fixtures/container_registry.rb
index d277373ecc6..2cb69024f20 100644
--- a/spec/frontend/fixtures/container_registry.rb
+++ b/spec/frontend/fixtures/container_registry.rb
@@ -32,6 +32,18 @@ RSpec.describe 'Container registry (JavaScript fixtures)', feature_category: :co
}
end
+ shared_examples 'container registry protection tag rules' do |fixture_suffix|
+ before do
+ create(:container_registry_protection_tag_rule, :immutable, project: project)
+ end
+
+ it "graphql/#{project_container_protection_tag_rules_query_path}.#{fixture_suffix}.json" do
+ post_graphql(query, current_user: user, variables: variables)
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
before do
stub_gitlab_api_client_to_support_gitlab_api(supported: true)
end
@@ -73,6 +85,18 @@ RSpec.describe 'Container registry (JavaScript fixtures)', feature_category: :co
end
end
+ context 'with immutable tag protection rules' do
+ it_behaves_like 'container registry protection tag rules', 'immutable_rules'
+ end
+
+ context 'with immutable tag protection rules as maintainer' do
+ before_all do
+ project.add_maintainer(user)
+ end
+
+ it_behaves_like 'container registry protection tag rules', 'immutable_rules_maintainer'
+ end
+
context 'with maximum number of tag protection rules' do
before do
5.times do |i|
diff --git a/spec/frontend/merge_request_dashboard/components/__snapshots__/collapsible_section_spec.js.snap b/spec/frontend/merge_request_dashboard/components/__snapshots__/collapsible_section_spec.js.snap
index 15d959933e6..bcd80453977 100644
--- a/spec/frontend/merge_request_dashboard/components/__snapshots__/collapsible_section_spec.js.snap
+++ b/spec/frontend/merge_request_dashboard/components/__snapshots__/collapsible_section_spec.js.snap
@@ -6,7 +6,7 @@ exports[`Merge request dashboard collapsible section renders section 1`] = `
id=""
>