Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-05-23 15:07:33 +00:00
parent 8e805f1259
commit e56bca3913
127 changed files with 1376 additions and 3349 deletions

View File

@ -1,5 +1,7 @@
trigger-ai-gateway-tagging:
stage: ai-gateway
# We need the tagging process to be executed even if other jobs in the gitlab pipeline fails
needs: []
image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}curlimages/curl:latest
script:
- |

View File

@ -3,10 +3,6 @@ include:
inputs:
gem_name: "mail-smtp_pool"
gem_path_prefix: "vendor/gems/"
- local: .gitlab/ci/templates/gem.gitlab-ci.yml
inputs:
gem_name: "attr_encrypted"
gem_path_prefix: "vendor/gems/"
- local: .gitlab/ci/templates/gem.gitlab-ci.yml
inputs:
gem_name: "microsoft_graph_mailer"

View File

@ -13,8 +13,8 @@ variables:
LFS_VERSION: "2.9"
NODE_VERSION: "20.12"
OS_VERSION: "bookworm"
RUBY_VERSION_DEFAULT: "3.2.6"
RUBY_VERSION_NEXT: "3.3.7"
RUBY_VERSION_DEFAULT: "3.2.8"
RUBY_VERSION_NEXT: "3.3.8"
RUBYGEMS_VERSION: "3.6"
RUST_VERSION: "1.73"
UBI_VERSION: "9.5"

View File

@ -1 +1 @@
3.3.7
3.3.8

View File

@ -1 +1 @@
de68b7ae147073d828434bcace613bc4a324b8b9
911b4a8ad2769faa16f732fcb7e381cdbc42902d

View File

@ -73,7 +73,7 @@ export default {
};
</script>
<template>
<div class="build-page gl-m-3">
<div class="build-page">
<log-viewer-top-bar :has-timestamps="hasTimestamps" />
<log-viewer :log="log" :loading="loading" />
</div>

View File

@ -138,6 +138,7 @@ export default {
:action-primary="$options.actionPrimary"
:action-secondary="$options.actionSecondary"
modal-class="set-user-status-modal"
visible
@primary="setStatus"
@secondary="removeStatus"
>

View File

@ -244,7 +244,8 @@ export default {
onShow() {
this.initBuyCIMinsCallout();
},
closeDropdown() {
openStatusModal() {
this.setStatusModalReady = true;
this.$refs.userDropdown.close();
},
initBuyCIMinsCallout() {
@ -314,11 +315,11 @@ export default {
<gl-disclosure-dropdown-group bordered>
<gl-disclosure-dropdown-item
v-if="setStatusModalReady && statusModalData"
v-if="statusModalData"
v-gl-modal="$options.SET_STATUS_MODAL_ID"
:item="statusItem"
data-testid="status-item"
@action="closeDropdown"
@action="openStatusModal"
/>
<gl-disclosure-dropdown-item
@ -383,10 +384,9 @@ export default {
/>
</gl-disclosure-dropdown>
<set-status-modal
v-if="statusModalData"
v-if="setStatusModalReady"
default-emoji="speech_balloon"
v-bind="statusModalData"
@mounted="setStatusModalReady = true"
/>
</div>
</template>

View File

@ -499,6 +499,7 @@ export default {
})
.finally(() => {
this.isLockDiscussionUpdating = false;
this.closeDropdown();
});
},
async promoteToObjective() {
@ -658,7 +659,7 @@ export default {
>
<template #list-item>
<gl-loading-icon v-if="isLockDiscussionUpdating" class="gl-mr-2" inline />
<gl-icon :name="lockDiscussionIcon" class="gl-mr-2" variant="subtle" />
<gl-icon v-else :name="lockDiscussionIcon" class="gl-mr-2" variant="subtle" />
{{ lockDiscussionText }}
</template>
</gl-disclosure-dropdown-item>

View File

@ -14,9 +14,13 @@ module Mutations
required: true,
description: 'Full Path of the project the settings belong to.'
argument :group_runners_enabled, GraphQL::Types::Boolean,
required: false,
description: 'Indicates whether group runners are enabled for the project.'
argument :keep_latest_artifact, GraphQL::Types::Boolean,
required: false,
description: 'Indicates if the latest artifact should be kept for the project.'
description: 'Indicates whether the latest artifact should be kept for the project.'
argument :job_token_scope_enabled, GraphQL::Types::Boolean,
required: false,
@ -24,12 +28,12 @@ module Mutations
reason: 'Outbound job token scope is being removed. This field can now only be set to false',
milestone: '16.0'
},
description: 'Indicates CI/CD job tokens generated in this project ' \
description: 'Indicates whether CI/CD job tokens generated in this project ' \
'have restricted access to other projects.'
argument :inbound_job_token_scope_enabled, GraphQL::Types::Boolean,
required: false,
description: 'Indicates CI/CD job tokens generated in other projects ' \
description: 'Indicates whether CI/CD job tokens generated in other projects ' \
'have restricted access to this project.'
argument :push_repository_for_job_token_allowed, GraphQL::Types::Boolean,
@ -79,6 +83,7 @@ module Mutations
def project_update_params(_project, **args)
{
group_runners_enabled: args[:group_runners_enabled],
keep_latest_artifact: args[:keep_latest_artifact],
ci_outbound_job_token_scope_enabled: args[:job_token_scope_enabled],
ci_inbound_job_token_scope_enabled: args[:inbound_job_token_scope_enabled],

View File

@ -49,6 +49,14 @@ module ResolvesPipelines
end
def resolve_pipelines(project, params = {})
Ci::PipelinesFinder.new(project, context[:current_user], params).execute
pipelines = Ci::PipelinesFinder.new(project, context[:current_user], params).execute
if %w[branches tags].include?(params[:scope])
# `branches` and `tags` scopes are ordered in a complex way that is not supported by the keyset pagination.
# We offset pagination here so we return the correct connection.
offset_pagination(pipelines)
else
pipelines
end
end
end

View File

@ -7,30 +7,35 @@ module Types
authorize :manage_merge_request_settings
field :group_runners_enabled,
GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether group runners are enabled for the project.',
authorize: :admin_project
field :inbound_job_token_scope_enabled,
GraphQL::Types::Boolean,
null: true,
description: 'Indicates CI/CD job tokens generated in other projects ' \
description: 'Indicates whether CI/CD job tokens generated in other projects ' \
'have restricted access to this project.',
method: :inbound_job_token_scope_enabled?,
authorize: :admin_project
field :job_token_scope_enabled,
GraphQL::Types::Boolean,
null: true,
description: 'Indicates CI/CD job tokens generated in this project ' \
description: 'Indicates whether CI/CD job tokens generated in this project ' \
'have restricted access to other projects.',
method: :job_token_scope_enabled?,
authorize: :admin_project
field :keep_latest_artifact,
GraphQL::Types::Boolean,
null: true,
description: 'Whether to keep the latest builds artifacts.',
description: 'Indicates whether the latest artifact should be kept for the project.',
method: :keep_latest_artifacts_available?,
authorize: :admin_project
field :merge_pipelines_enabled,
GraphQL::Types::Boolean,
null: true,
description: 'Whether merged results pipelines are enabled.',
description: 'Indicates whether merged results pipelines are enabled.',
method: :merge_pipelines_enabled?
field :pipeline_variables_minimum_override_role,
GraphQL::Types::String,

View File

@ -409,6 +409,7 @@ module Types
field :pipelines,
null: true,
calls_gitaly: true,
description: 'Pipelines of the project.',
extras: [:lookahead],
resolver: Resolvers::Ci::ProjectPipelinesResolver

View File

@ -136,6 +136,10 @@ module Ci
.transform_values { |s| Ci::RunnerVersion.statuses.key(s).to_sym }
end
def self.ip_address_exists?(ip_address)
exists?(ip_address:)
end
def uncached_contacted_at
read_attribute(:contacted_at)
end

View File

@ -26,8 +26,7 @@ module ClickHouse
reached_end_of_table: context.no_more_records?)
if context.last_processed_id
ClickHouse::SyncCursor.update_cursor_for(model_class.table_name,
context.last_processed_id)
ClickHouse::SyncCursor.update_cursor_for(sync_cursor_identifier, context.last_processed_id)
end
end
rescue Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
@ -46,7 +45,7 @@ module ClickHouse
def context
@context ||= ClickHouse::RecordSyncContext.new(
last_record_id: ClickHouse::SyncCursor.cursor_for(model_class.table_name),
last_record_id: ClickHouse::SyncCursor.cursor_for(sync_cursor_identifier),
max_records_per_batch: INSERT_BATCH_SIZE,
runtime_limiter: Gitlab::Metrics::RuntimeLimiter.new(MAX_RUNTIME)
)
@ -118,6 +117,10 @@ module ClickHouse
:id
end
def sync_cursor_identifier
model_class.table_name
end
def csv_mapping
raise NotImplementedError, "Subclasses must implement `csv_mapping`"
end

View File

@ -1,4 +1,4 @@
- @no_container = true
- @force_fluid_layout = true
- add_to_breadcrumbs _("Jobs"), project_jobs_path(@project)
- add_to_breadcrumbs "##{@build.id}", project_job_path(@project, @build)
- breadcrumb_title s_("Job|Full log viewer")

View File

@ -113,6 +113,16 @@
:idempotent: false
:tags: []
:queue_namespace: :batched_background_migrations
- :name: batched_background_migrations:database_batched_background_migration_sec_execution
:worker_name: Database::BatchedBackgroundMigration::SecExecutionWorker
:feature_category: :database
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: false
:tags: []
:queue_namespace: :batched_background_migrations
- :name: chaos:chaos_cpu_spin
:worker_name: Chaos::CpuSpinWorker
:feature_category: :not_owned
@ -526,6 +536,16 @@
:idempotent: true
:tags: []
:queue_namespace: :cronjob
- :name: cronjob:database_batched_background_migration_sec_database
:worker_name: Database::BatchedBackgroundMigration::SecDatabaseWorker
:feature_category: :database
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
:queue_namespace: :cronjob
- :name: cronjob:database_ci_namespace_mirrors_consistency_check
:worker_name: Database::CiNamespaceMirrorsConsistencyCheckWorker
:feature_category: :cell

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Database # rubocop:disable Gitlab/BoundedContexts -- Doesn't make sense to put this elsewhere
module BatchedBackgroundMigration
class SecDatabaseWorker # rubocop:disable Scalability/IdempotentWorker -- SingleDatabaseWorker is idempotent!
include SingleDatabaseWorker
def self.tracking_database
@tracking_database ||= Gitlab::Database::SEC_DATABASE_NAME.to_sym
end
def execution_worker_class
@execution_worker_class ||= Database::BatchedBackgroundMigration::SecExecutionWorker
end
end
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module Database # rubocop:disable Gitlab/BoundedContexts -- Doesn't make sense to put this elsewhere
module BatchedBackgroundMigration
class SecExecutionWorker # rubocop:disable Scalability/IdempotentWorker -- Not guaranteed to be idempotent
include ExecutionWorker
end
end
end

View File

@ -32,6 +32,7 @@ domains:
- code_suggestions # Also in CodeSuggestions
- cloud_connector # Also in CloudConnector
- duo_workflow
- knowledge_graph
Analytics:
description:

View File

@ -0,0 +1,23 @@
---
description: REST API endpoint requested from runner
internal_events: true
action: api_request_from_runner
identifiers:
- project
- namespace
additional_properties:
label:
description: The API endpoint of the request
property:
description: The token type used to authenticate
cross_project_request:
description: The request is cross-project
product_group: authorization
product_categories:
- permissions
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
tiers:
- free
- premium
- ultimate

View File

@ -37,6 +37,7 @@
- code_testing
- compliance_management
- component_catalog
- configuration
- consumables_cost_management
- container_registry
- container_scanning
@ -57,6 +58,7 @@
- disaster_recovery
- dora_metrics
- duo_chat
- duo_setting
- duo_workflow
- dynamic_application_security_testing
- editor_extensions
@ -84,6 +86,7 @@
- integrations
- internationalization
- job_artifacts
- knowledge_graph
- markdown
- merge_trains
- mlops

View File

@ -0,0 +1,10 @@
---
name: track_api_request_from_runner
description: Track API requests from runners
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/525339
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/538150
milestone: '18.1'
group: group::authorization
type: gitlab_com_derisk
default_enabled: false

View File

@ -513,6 +513,10 @@ p_ci_workloads:
- table: projects
column: project_id
on_delete: async_delete
p_knowledge_graph_replicas:
- table: p_knowledge_graph_enabled_namespaces
column: knowledge_graph_enabled_namespace_id
on_delete: async_nullify
packages_build_infos:
- table: p_ci_pipelines
column: pipeline_id

View File

@ -681,6 +681,9 @@ Settings.cron_jobs['batched_background_migrations_worker']['job_class'] = 'Datab
Settings.cron_jobs['batched_background_migration_worker_ci_database'] ||= {}
Settings.cron_jobs['batched_background_migration_worker_ci_database']['cron'] ||= '* * * * *'
Settings.cron_jobs['batched_background_migration_worker_ci_database']['job_class'] = 'Database::BatchedBackgroundMigration::CiDatabaseWorker'
Settings.cron_jobs['batched_background_migration_worker_sec_database'] ||= {}
Settings.cron_jobs['batched_background_migration_worker_sec_database']['cron'] ||= '* * * * *'
Settings.cron_jobs['batched_background_migration_worker_sec_database']['job_class'] = 'Database::BatchedBackgroundMigration::SecDatabaseWorker'
Settings.cron_jobs['issues_reschedule_stuck_issue_rebalances'] ||= {}
Settings.cron_jobs['issues_reschedule_stuck_issue_rebalances']['cron'] ||= '*/15 * * * *'
Settings.cron_jobs['issues_reschedule_stuck_issue_rebalances']['job_class'] = 'Issues::RescheduleStuckIssueRebalancesWorker'
@ -914,6 +917,9 @@ Gitlab.ee do
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker'] ||= {}
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker']['job_class'] = 'Vulnerabilities::OrphanedRemediationsCleanupWorker'
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker']['cron'] ||= '15 3 * * */6'
Settings.cron_jobs['security_analyzer_namespace_statuses_schedule_worker'] ||= {}
Settings.cron_jobs['security_analyzer_namespace_statuses_schedule_worker']['cron'] ||= '0 8 * * 0'
Settings.cron_jobs['security_analyzer_namespace_statuses_schedule_worker']['job_class'] = 'Security::AnalyzerNamespaceStatuses::ScheduleWorker'
Settings.cron_jobs['security_create_orchestration_policy_worker'] ||= {}
Settings.cron_jobs['security_create_orchestration_policy_worker']['cron'] ||= '*/10 * * * *'
Settings.cron_jobs['security_create_orchestration_policy_worker']['job_class'] = 'Security::CreateOrchestrationPolicyWorker'

View File

@ -54,7 +54,9 @@ if Gitlab.ee?
Ai::TroubleshootJobEvent,
Vulnerabilities::Archive,
Vulnerabilities::ArchivedRecord,
Vulnerabilities::ArchiveExport
Vulnerabilities::ArchiveExport,
Ai::KnowledgeGraph::EnabledNamespace,
Ai::KnowledgeGraph::Replica
])
else
Gitlab::Database::Partitioning.register_tables(

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_ci_job_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: ci_job_token

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_cluster_agent_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: cluster_agent_token

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_deploy_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: deploy_token

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_group_access_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: group_access_token

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_oauth_application_secrets_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: oauth_application_secret

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_personal_access_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: personal_access_token

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_project_access_tokens_from_api_request_from_runner
description: Count of unique REST API endpoints requested from CI/CD runners
product_group: authorization
product_categories:
- permissions
performance_indicator_type: []
value_type: number
status: active
milestone: '18.1'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189430
time_frame:
- 28d
- 7d
data_source: internal_events
data_category: standard
tiers:
- free
- premium
- ultimate
events:
- name: api_request_from_runner
filter:
property: project_access_token

View File

@ -877,6 +877,8 @@
- 1
- - secrets_management_provision_project_secrets_manager
- 1
- - security_analyzer_namespace_statuses_adjustment
- 1
- - security_analyzers_status_process_archived_events
- 1
- - security_configuration_set_group_secret_push_protection

View File

@ -0,0 +1,13 @@
---
table_name: p_knowledge_graph_enabled_namespaces
classes:
- Ai::KnowledgeGraph::EnabledNamespace
feature_categories:
- knowledge_graph
description: Represents a namespace with enabled knowledge graph
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/191162
milestone: '18.1'
gitlab_schema: gitlab_main_cell
sharding_key:
namespace_id: namespaces
table_size: small

View File

@ -0,0 +1,11 @@
---
table_name: p_knowledge_graph_replicas
classes:
- Ai::KnowledgeGraph::Replica
feature_categories:
- knowledge_graph
description: Represents a replica of a namespace on a Zoekt node
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/191162
milestone: '18.1'
gitlab_schema: gitlab_main_cell_local
table_size: small

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
class AddKnowledgeGraphEnabledNamespaces < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
milestone '18.1'
TABLE_NAME = :p_knowledge_graph_enabled_namespaces
PARTITION_SIZE = 2_000_000
MIN_ID = Namespace.connection
.select_value("select min_value from pg_sequences where sequencename = 'namespaces_id_seq'") || 1
def up
with_lock_retries do
create_table TABLE_NAME,
options: 'PARTITION BY RANGE (namespace_id)',
primary_key: [:id, :namespace_id], if_not_exists: true do |t|
t.bigserial :id, null: false
t.bigint :namespace_id, null: false
t.timestamps_with_timezone null: false
t.integer :state, null: false, default: 0, limit: 2, index: true
t.index :namespace_id, unique: true
end
end
create_partitions
end
def down
drop_table TABLE_NAME
end
private
def create_partitions
max_id = Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection.with_suppressed do
define_batchable_model('namespaces', connection: connection).maximum(:id) || MIN_ID
end
end
create_int_range_partitions(TABLE_NAME, PARTITION_SIZE, MIN_ID, max_id)
end
end

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
class AddKnowledgeGraphReplicas < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
milestone '18.1'
TABLE_NAME = :p_knowledge_graph_replicas
PARTITION_SIZE = 2_000_000
MIN_ID = Namespace.connection
.select_value("select min_value from pg_sequences where sequencename = 'namespaces_id_seq'") || 1
def up
with_lock_retries do
create_table TABLE_NAME,
options: 'PARTITION BY RANGE (namespace_id)',
primary_key: [:id, :namespace_id], if_not_exists: true do |t|
t.bigserial :id, null: false
t.bigint :namespace_id, null: false
t.bigint :knowledge_graph_enabled_namespace_id, null: true
t.bigint :zoekt_node_id, null: false, index: true
t.timestamps_with_timezone null: false
t.integer :state, null: false, index: true, default: 0, limit: 2
t.integer :retries_left, limit: 2, null: false
t.index :namespace_id, unique: true,
name: 'index_p_knowledge_graph_replicas_on_namespace_id', using: :btree
t.check_constraint 'retries_left > 0 OR retries_left = 0 AND state >= 200',
name: 'c_p_knowledge_graph_replicas_retries_status'
end
end
create_partitions
end
def down
drop_table TABLE_NAME
end
private
def create_partitions
max_id = Gitlab::Database::QueryAnalyzers::RestrictAllowedSchemas.with_suppressed do
Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection.with_suppressed do
define_batchable_model('namespaces', connection: connection).maximum(:id) || MIN_ID
end
end
create_int_range_partitions(TABLE_NAME, PARTITION_SIZE, MIN_ID, max_id)
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class CreateKnowledgeGraphNamespaceForeignKey < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers
disable_ddl_transaction!
milestone '18.1'
def up
add_concurrent_partitioned_foreign_key :p_knowledge_graph_enabled_namespaces, :namespaces,
column: :namespace_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :p_knowledge_graph_enabled_namespaces, column: :namespace_id
end
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class CreateKnowledgeGraphReplicaNodeForeignKey < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers
disable_ddl_transaction!
milestone '18.1'
def up
add_concurrent_partitioned_foreign_key :p_knowledge_graph_replicas, :zoekt_nodes,
column: :zoekt_node_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key :p_knowledge_graph_replicas, column: :zoekt_node_id
end
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class CreateKnowledgeGraphReplicaNamespaceForeignKey < Gitlab::Database::Migration[2.3]
include Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers
milestone '18.1'
def up
track_record_deletions_override_table_name(:p_knowledge_graph_enabled_namespaces)
end
def down
untrack_record_deletions(:p_knowledge_graph_enabled_namespaces)
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class AddKnowledgeGraphReplicasIndex < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
milestone '18.1'
disable_ddl_transaction!
TABLE_NAME = :p_knowledge_graph_replicas
INDEX_NAME = :p_knowledge_graph_replicas_namespace_id_and_zoekt_node_id
def up
add_concurrent_partitioned_index TABLE_NAME,
[:knowledge_graph_enabled_namespace_id, :zoekt_node_id, :namespace_id], unique: true,
name: INDEX_NAME
end
def down
remove_concurrent_partitioned_index_by_name(TABLE_NAME, INDEX_NAME)
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddIndexToCiRunnerMachinesOnIpAddress < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
disable_ddl_transaction!
milestone '18.1'
INDEX_NAME = :index_ci_runner_machines_on_ip_address
def up
add_concurrent_partitioned_index :ci_runner_machines, :ip_address, name: INDEX_NAME
end
def down
remove_concurrent_partitioned_index_by_name :ci_runner_machines, INDEX_NAME
end
end

View File

@ -0,0 +1 @@
7e7bf46103dcffb6354e6f4aa27936cc48a3a8fa66b2e08ace18b8fd7e6d7036

View File

@ -0,0 +1 @@
2dd1e12f140324e75d05581d5016749ad202045ecde8d983446c3e97cb2ef91c

View File

@ -0,0 +1 @@
aa3ead0fab83df13bce2342c2b52f7f9cacbe69635995587e9db66b1a0dfbee5

View File

@ -0,0 +1 @@
a78d3176900873832c9a584e8ccfdfa688a36b6fc45dfc9a3f07ac6f0054559d

View File

@ -0,0 +1 @@
1807d47c2367f22d8979dcc3b08506771e5712ea688695a5ec37ce7abd815ae3

View File

@ -0,0 +1 @@
cdbcfd9f7f540d5ad3cb02161feae24a919658709cbe70e021a25cdd7c5e428d

View File

@ -0,0 +1 @@
313e66083d31bb2341052672ee2adc1f4d39f91844ae64fc4c639c8fe7a12ef8

View File

@ -4880,6 +4880,28 @@ CREATE TABLE p_ci_finished_pipeline_ch_sync_events (
)
PARTITION BY LIST (partition);
CREATE TABLE p_knowledge_graph_enabled_namespaces (
id bigint NOT NULL,
namespace_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
state smallint DEFAULT 0 NOT NULL
)
PARTITION BY RANGE (namespace_id);
CREATE TABLE p_knowledge_graph_replicas (
id bigint NOT NULL,
namespace_id bigint NOT NULL,
knowledge_graph_enabled_namespace_id bigint,
zoekt_node_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
state smallint DEFAULT 0 NOT NULL,
retries_left smallint NOT NULL,
CONSTRAINT c_p_knowledge_graph_replicas_retries_status CHECK (((retries_left > 0) OR ((retries_left = 0) AND (state >= 200))))
)
PARTITION BY RANGE (namespace_id);
CREATE TABLE project_audit_events (
id bigint DEFAULT nextval('shared_audit_event_id_seq'::regclass) NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -18896,6 +18918,24 @@ CREATE SEQUENCE p_ci_workloads_id_seq
ALTER SEQUENCE p_ci_workloads_id_seq OWNED BY p_ci_workloads.id;
CREATE SEQUENCE p_knowledge_graph_enabled_namespaces_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE p_knowledge_graph_enabled_namespaces_id_seq OWNED BY p_knowledge_graph_enabled_namespaces.id;
CREATE SEQUENCE p_knowledge_graph_replicas_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE p_knowledge_graph_replicas_id_seq OWNED BY p_knowledge_graph_replicas.id;
CREATE TABLE packages_build_infos (
id bigint NOT NULL,
package_id bigint NOT NULL,
@ -27669,6 +27709,10 @@ ALTER TABLE ONLY p_ci_builds_metadata ALTER COLUMN id SET DEFAULT nextval('ci_bu
ALTER TABLE ONLY p_ci_workloads ALTER COLUMN id SET DEFAULT nextval('p_ci_workloads_id_seq'::regclass);
ALTER TABLE ONLY p_knowledge_graph_enabled_namespaces ALTER COLUMN id SET DEFAULT nextval('p_knowledge_graph_enabled_namespaces_id_seq'::regclass);
ALTER TABLE ONLY p_knowledge_graph_replicas ALTER COLUMN id SET DEFAULT nextval('p_knowledge_graph_replicas_id_seq'::regclass);
ALTER TABLE ONLY packages_build_infos ALTER COLUMN id SET DEFAULT nextval('packages_build_infos_id_seq'::regclass);
ALTER TABLE ONLY packages_conan_file_metadata ALTER COLUMN id SET DEFAULT nextval('packages_conan_file_metadata_id_seq'::regclass);
@ -30433,6 +30477,12 @@ ALTER TABLE ONLY p_ci_stages
ALTER TABLE ONLY p_ci_workloads
ADD CONSTRAINT p_ci_workloads_pkey PRIMARY KEY (id, partition_id);
ALTER TABLE ONLY p_knowledge_graph_enabled_namespaces
ADD CONSTRAINT p_knowledge_graph_enabled_namespaces_pkey PRIMARY KEY (id, namespace_id);
ALTER TABLE ONLY p_knowledge_graph_replicas
ADD CONSTRAINT p_knowledge_graph_replicas_pkey PRIMARY KEY (id, namespace_id);
ALTER TABLE ONLY packages_build_infos
ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id);
@ -33672,6 +33722,10 @@ CREATE INDEX index_ci_runner_machines_on_executor_type ON ONLY ci_runner_machine
CREATE INDEX index_012094097c ON instance_type_ci_runner_machines USING btree (executor_type);
CREATE INDEX index_ci_runner_machines_on_ip_address ON ONLY ci_runner_machines USING btree (ip_address);
CREATE INDEX index_053d12f7ee ON project_type_ci_runner_machines USING btree (ip_address);
CREATE INDEX index_p_ci_builds_on_execution_config_id ON ONLY p_ci_builds USING btree (execution_config_id) WHERE (execution_config_id IS NOT NULL);
CREATE INDEX index_0928d9f200 ON ci_builds USING btree (execution_config_id) WHERE (execution_config_id IS NOT NULL);
@ -34772,6 +34826,8 @@ CREATE UNIQUE INDEX index_customer_relations_contacts_on_unique_email_per_group
CREATE UNIQUE INDEX index_cycle_analytics_stage_event_hashes_on_org_id_sha_256 ON analytics_cycle_analytics_stage_event_hashes USING btree (organization_id, hash_sha256);
CREATE INDEX index_d2746151f0 ON instance_type_ci_runner_machines USING btree (ip_address);
CREATE INDEX index_d58435d85e ON project_type_ci_runner_machines USING btree (executor_type);
CREATE INDEX p_ci_pipelines_trigger_id_id_desc_idx ON ONLY p_ci_pipelines USING btree (trigger_id, id DESC);
@ -35044,6 +35100,8 @@ CREATE INDEX index_early_access_program_tracking_events_on_event_name ON early_a
CREATE INDEX index_early_access_program_tracking_events_on_user_id ON early_access_program_tracking_events USING btree (user_id);
CREATE INDEX index_ee7c87e634 ON group_type_ci_runner_machines USING btree (ip_address);
CREATE UNIQUE INDEX index_elastic_index_settings_on_alias_name ON elastic_index_settings USING btree (alias_name);
CREATE INDEX index_elastic_reindexing_subtasks_on_elastic_reindexing_task_id ON elastic_reindexing_subtasks USING btree (elastic_reindexing_task_id);
@ -36436,6 +36494,16 @@ CREATE INDEX index_p_ci_runner_machine_builds_on_runner_machine_id ON ONLY p_ci_
CREATE INDEX index_p_ci_workloads_on_project_id ON ONLY p_ci_workloads USING btree (project_id);
CREATE UNIQUE INDEX index_p_knowledge_graph_enabled_namespaces_on_namespace_id ON ONLY p_knowledge_graph_enabled_namespaces USING btree (namespace_id);
CREATE INDEX index_p_knowledge_graph_enabled_namespaces_on_state ON ONLY p_knowledge_graph_enabled_namespaces USING btree (state);
CREATE UNIQUE INDEX index_p_knowledge_graph_replicas_on_namespace_id ON ONLY p_knowledge_graph_replicas USING btree (namespace_id);
CREATE INDEX index_p_knowledge_graph_replicas_on_state ON ONLY p_knowledge_graph_replicas USING btree (state);
CREATE INDEX index_p_knowledge_graph_replicas_on_zoekt_node_id ON ONLY p_knowledge_graph_replicas USING btree (zoekt_node_id);
CREATE INDEX index_packages_build_infos_on_pipeline_id ON packages_build_infos USING btree (pipeline_id);
CREATE INDEX index_packages_build_infos_on_project_id ON packages_build_infos USING btree (project_id);
@ -38428,6 +38496,8 @@ CREATE INDEX p_ci_stages_project_id_idx ON ONLY p_ci_stages USING btree (project
CREATE UNIQUE INDEX p_ci_workloads_pipeline_id_idx ON ONLY p_ci_workloads USING btree (pipeline_id, partition_id);
CREATE UNIQUE INDEX p_knowledge_graph_replicas_namespace_id_and_zoekt_node_id ON ONLY p_knowledge_graph_replicas USING btree (knowledge_graph_enabled_namespace_id, zoekt_node_id, namespace_id);
CREATE INDEX package_name_index ON packages_packages USING btree (name);
CREATE INDEX packages_packages_failed_verification ON packages_package_files USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
@ -40768,6 +40838,8 @@ ALTER INDEX index_uploads_9ba88c4165_on_uploader_and_path ATTACH PARTITION impor
ALTER INDEX index_ci_runner_machines_on_executor_type ATTACH PARTITION index_012094097c;
ALTER INDEX index_ci_runner_machines_on_ip_address ATTACH PARTITION index_053d12f7ee;
ALTER INDEX index_p_ci_builds_on_execution_config_id ATTACH PARTITION index_0928d9f200;
ALTER INDEX index_ci_runner_machines_on_executor_type ATTACH PARTITION index_aa3b4fe8c6;
@ -40850,10 +40922,14 @@ ALTER INDEX p_ci_pipelines_user_id_id_idx ATTACH PARTITION index_ci_pipelines_on
ALTER INDEX p_ci_pipelines_user_id_id_idx1 ATTACH PARTITION index_ci_pipelines_on_user_id_and_id_desc_and_user_not_verified;
ALTER INDEX index_ci_runner_machines_on_ip_address ATTACH PARTITION index_d2746151f0;
ALTER INDEX index_ci_runner_machines_on_executor_type ATTACH PARTITION index_d58435d85e;
ALTER INDEX p_ci_pipelines_trigger_id_id_desc_idx ATTACH PARTITION index_d8ae6ea3f3;
ALTER INDEX index_ci_runner_machines_on_ip_address ATTACH PARTITION index_ee7c87e634;
ALTER INDEX p_ci_builds_user_id_name_idx ATTACH PARTITION index_partial_ci_builds_on_user_id_name_parser_features;
ALTER INDEX p_ci_builds_user_id_name_created_at_idx ATTACH PARTITION index_secure_ci_builds_on_user_id_name_created_at;
@ -41288,6 +41364,8 @@ CREATE TRIGGER p_ci_pipelines_loose_fk_trigger AFTER DELETE ON p_ci_pipelines RE
CREATE TRIGGER p_ci_workloads_loose_fk_trigger AFTER DELETE ON p_ci_workloads REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records_override_table('p_ci_workloads');
CREATE TRIGGER p_knowledge_graph_enabled_namespaces_loose_fk_trigger AFTER DELETE ON p_knowledge_graph_enabled_namespaces REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records_override_table('p_knowledge_graph_enabled_namespaces');
CREATE TRIGGER plans_loose_fk_trigger AFTER DELETE ON plans REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
CREATE TRIGGER pool_repositories_loose_fk_trigger AFTER DELETE ON pool_repositories REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
@ -44550,6 +44628,9 @@ ALTER TABLE ONLY duo_workflows_checkpoint_writes
ALTER TABLE ONLY issuable_resource_links
ADD CONSTRAINT fk_rails_3f0ec6b1cf FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
ALTER TABLE p_knowledge_graph_replicas
ADD CONSTRAINT fk_rails_3f20642c2f FOREIGN KEY (zoekt_node_id) REFERENCES zoekt_nodes(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_cycle_analytics_stage_aggregations
ADD CONSTRAINT fk_rails_3f409802fc FOREIGN KEY (stage_id) REFERENCES analytics_cycle_analytics_group_stages(id) ON DELETE CASCADE;
@ -45054,6 +45135,9 @@ ALTER TABLE ONLY personal_access_token_last_used_ips
ALTER TABLE ONLY clusters_kubernetes_namespaces
ADD CONSTRAINT fk_rails_7e7688ecaf FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE p_knowledge_graph_enabled_namespaces
ADD CONSTRAINT fk_rails_801c561c42 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY security_policies
ADD CONSTRAINT fk_rails_802ceea0c8 FOREIGN KEY (security_orchestration_policy_configuration_id) REFERENCES security_orchestration_policy_configurations(id) ON DELETE CASCADE;

View File

@ -0,0 +1,99 @@
---
stage: Tenant Scale
group: Organizations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
gitlab_dedicated: yes
title: Dormant project deletion
---
{{< details >}}
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed, GitLab Dedicated
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](feature_flags.md) named `inactive_projects_deletion`. Disabled by default.
- [Feature flag `inactive_projects_deletion`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) removed in GitLab 15.4.
- Configuration through GitLab UI [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85575) in GitLab 15.1.
- [Renamed](https://gitlab.com/gitlab-org/gitlab/-/work_items/533275) from inactive project deletion in GitLab 18.1.
{{< /history >}}
Over time, projects in large GitLab instances can become dormant and use unnecessary disk space.
You can configure GitLab to automatically delete dormant projects after a specific period of inactivity.
When a project has no activity within this defined period:
- Maintainers receive notifications that warn about the scheduled deletion.
- If no activity occurs in the project, GitLab deletes it when the timeframe expires.
- When deletion occurs, GitLab generates an audit event that shows @GitLab-Admin-Bot performed the deletion.
For the default setting on GitLab.com, see [GitLab.com settings](../user/gitlab_com/_index.md#dormant-project-deletion).
## Configure dormant project deletion
To configure deletion of dormant projects:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Repository**.
1. Expand **Repository maintenance**.
1. In the **Dormant project deletion** section, select **Delete dormant projects**.
1. Configure the settings.
- The warning email is sent to users who have the Owner and Maintainer role for the dormant project.
- The email duration must be less than the **Delete project after** duration.
1. Select **Save changes**.
Dormant projects that meet the criteria are scheduled for deletion and a warning email is sent. If the
projects remain dormant, they are deleted after the specified duration. These projects are deleted even if
[the project is archived](../user/project/working_with_projects.md#archive-a-project).
### Configuration example
#### Example 1
If you use these settings:
- **Delete dormant projects** enabled.
- **Delete dormant projects that exceed** set to `50`.
- **Delete project after** set to `12`.
- **Send warning email** set to `6`.
If a project is less than 50 MB, the project is not considered dormant.
If a project is more than 50 MB and it is dormant for:
- More than 6 months: A deletion warning email is sent. This email includes the date at which the project will be scheduled for deletion.
- More than 12 months: The project is scheduled for deletion.
#### Example 2
If you use these settings:
- **Delete dormant projects** enabled.
- **Delete dormant projects that exceed** set to `0`.
- **Delete project after** set to `12`.
- **Send warning email** set to `11`.
Because the size limit has been set to 0 MB, all projects in an instance are covered.
If a project is dormant for:
- More than 11 months: A deletion warning email is sent. This email includes the date at which the project will be scheduled for deletion.
- More than 12 months: The project is scheduled for deletion.
If a project exists that has already been dormant for more than 12 months when you configure these settings:
- A deletion warning email is sent immediately. This email includes the date at which the project will be scheduled for deletion.
- The project is scheduled for deletion 1 month (12 months - 11 months) after the warning email has been sent.
## Determine when a project was last active
You can view a project's activities and determine when the project was last active in the following ways:
- Go to the [activity page](../user/project/working_with_projects.md#view-project-activity) for the project and view
the date of the latest event.
- View the `last_activity_at` attribute for the project using the [Projects API](../api/projects.md).
- List the visible events for the project using the [Events API](../api/events.md#list-all-visible-events-for-a-project).
View the `created_at` attribute of the latest event.

View File

@ -176,7 +176,7 @@ To trigger a manual prune of unreachable objects:
While GitLab automatically performs housekeeping tasks based on the number of
pushes, it does not maintain repositories that don't receive any pushes at all.
As a result, inactive repositories or repositories that are only getting read
As a result, dormant repositories or repositories that are only getting read
requests may not benefit from improvements in the repository housekeeping
strategy.

View File

@ -1,91 +1,13 @@
---
stage: Tenant Scale
group: Organizations
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
gitlab_dedicated: yes
title: Dormant project deletion
redirect_to: 'dormant_project_deletion.md'
remove_date: '2025-08-20'
---
{{< details >}}
<!-- markdownlint-disable -->
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed, GitLab Dedicated
This document was moved to [another location](dormant_project_deletion.md).
{{< /details >}}
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](feature_flags.md) named `inactive_projects_deletion`. Disabled by default.
- [Feature flag `inactive_projects_deletion`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) removed in GitLab 15.4.
- Configuration through GitLab UI [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85575) in GitLab 15.1.
- [Renamed](https://gitlab.com/gitlab-org/gitlab/-/work_items/533275) from inactive project deletion in GitLab 18.1.
{{< /history >}}
Administrators of large GitLab instances can find that over time, projects become dormant and are no longer used.
These projects take up unnecessary disk space.
With dormant project deletion, you can identify these projects, warn the maintainers ahead of time, and then delete the
projects if they remain dormant. When an dormant project is deleted, the action generates an audit event that it was
performed by the @GitLab-Admin-Bot.
For the default setting on GitLab.com, see the [GitLab.com settings page](../user/gitlab_com/_index.md#inactive-project-deletion).
## Configure dormant project deletion
To configure deletion of dormant projects:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Repository**.
1. Expand **Repository maintenance**.
1. In the **Dormant project deletion** section, select **Delete dormant projects**.
1. Configure the settings.
- The warning email is sent to users who have the Owner and Maintainer role for the dormant project.
- The email duration must be less than the **Delete project after** duration.
1. Select **Save changes**.
Dormant projects that meet the criteria are scheduled for deletion and a warning email is sent. If the
projects remain dormant, they are deleted after the specified duration. These projects are deleted even if
[the project is archived](../user/project/working_with_projects.md#archive-a-project).
### Configuration example
#### Example 1
If you use these settings:
- **Delete dormant projects** enabled.
- **Delete dormant projects that exceed** set to `50`.
- **Delete project after** set to `12`.
- **Send warning email** set to `6`.
If a project is less than 50 MB, the project is not considered dormant.
If a project is more than 50 MB and it is dormant for:
- More than 6 months: A deletion warning email is sent. This mail includes the date that the project will be deleted.
- More than 12 months: The project is scheduled for deletion.
#### Example 2
If you use these settings:
- **Delete dormant projects** enabled.
- **Delete dormant projects that exceed** set to `0`.
- **Delete project after** set to `12`.
- **Send warning email** set to `11`.
If a project exists that has already been dormant for more than 12 months when you configure these settings:
- A deletion warning email is sent immediately. This email includes the date that the project will be deleted.
- The project is scheduled for deletion 1 month (12 months - 11 months) after warning email.
## Determine when a project was last active
You can view a project's activities and determine when the project was last active in the following ways:
- Go to the [activity page](../user/project/working_with_projects.md#view-project-activity) for the project and view
the date of the latest event.
- View the `last_activity_at` attribute for the project using the [Projects API](../api/projects.md).
- List the visible events for the project using the [Events API](../api/events.md#list-all-visible-events-for-a-project).
View the `created_at` attribute of the latest event.
<!-- This redirect file can be deleted after <2025-08-20>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/development/documentation/redirects -->

View File

@ -58,7 +58,7 @@ is an internal user that cannot be accessed or modified by regular users and is
projects.
- [Automatically deactivating dormant users](moderate_users.md#automatically-deactivate-dormant-users).
- [Automatically deleting unconfirmed users](moderate_users.md#automatically-delete-unconfirmed-users).
- [Deleting inactive projects](inactive_project_deletion.md).
- [Deleting dormant projects](dormant_project_deletion.md).
- [Locking users](../security/unlock_user.md).
## GitLab Security Bot

View File

@ -25,7 +25,7 @@ Keep your GitLab instance up and running.
- [Enable encrypted configuration](../encrypted_configuration.md)
- [Rake tasks](../../raketasks/_index.md)
- [Backup and restore](../backup_restore/_index.md)
- [Inactive project deletion](../inactive_project_deletion.md)
- [Dormant project deletion](../dormant_project_deletion.md)
- [Move repositories](moving_repositories.md)
- [Read-only state](../read_only_gitlab.md)
- [Restart GitLab](../restart_gitlab.md)

View File

@ -9263,9 +9263,10 @@ Input type: `ProjectCiCdSettingsUpdateInput`
| ---- | ---- | ----------- |
| <a id="mutationprojectcicdsettingsupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationprojectcicdsettingsupdatefullpath"></a>`fullPath` | [`ID!`](#id) | Full Path of the project the settings belong to. |
| <a id="mutationprojectcicdsettingsupdateinboundjobtokenscopeenabled"></a>`inboundJobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates CI/CD job tokens generated in other projects have restricted access to this project. |
| <a id="mutationprojectcicdsettingsupdategrouprunnersenabled"></a>`groupRunnersEnabled` | [`Boolean`](#boolean) | Indicates whether group runners are enabled for the project. |
| <a id="mutationprojectcicdsettingsupdateinboundjobtokenscopeenabled"></a>`inboundJobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates whether CI/CD job tokens generated in other projects have restricted access to this project. |
| <a id="mutationprojectcicdsettingsupdatejobtokenscopeenabled"></a>`jobTokenScopeEnabled` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Deprecated**: Outbound job token scope is being removed. This field can now only be set to false. Deprecated in GitLab 16.0. |
| <a id="mutationprojectcicdsettingsupdatekeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Indicates if the latest artifact should be kept for the project. |
| <a id="mutationprojectcicdsettingsupdatekeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Indicates whether the latest artifact should be kept for the project. |
| <a id="mutationprojectcicdsettingsupdatemergepipelinesenabled"></a>`mergePipelinesEnabled` | [`Boolean`](#boolean) | Indicates if merged results pipelines are enabled for the project. |
| <a id="mutationprojectcicdsettingsupdatemergetrainsenabled"></a>`mergeTrainsEnabled` | [`Boolean`](#boolean) | Indicates if merge trains are enabled for the project. |
| <a id="mutationprojectcicdsettingsupdatemergetrainsskiptrainallowed"></a>`mergeTrainsSkipTrainAllowed` | [`Boolean`](#boolean) | Indicates whether an option is allowed to merge without refreshing the merge train. Ignored unless the `merge_trains_skip_train` feature flag is also enabled. |
@ -37792,10 +37793,11 @@ four standard [pagination arguments](#pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectcicdsettinginboundjobtokenscopeenabled"></a>`inboundJobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates CI/CD job tokens generated in other projects have restricted access to this project. |
| <a id="projectcicdsettingjobtokenscopeenabled"></a>`jobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates CI/CD job tokens generated in this project have restricted access to other projects. |
| <a id="projectcicdsettingkeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Whether to keep the latest builds artifacts. |
| <a id="projectcicdsettingmergepipelinesenabled"></a>`mergePipelinesEnabled` | [`Boolean`](#boolean) | Whether merged results pipelines are enabled. |
| <a id="projectcicdsettinggrouprunnersenabled"></a>`groupRunnersEnabled` | [`Boolean`](#boolean) | Indicates whether group runners are enabled for the project. |
| <a id="projectcicdsettinginboundjobtokenscopeenabled"></a>`inboundJobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates whether CI/CD job tokens generated in other projects have restricted access to this project. |
| <a id="projectcicdsettingjobtokenscopeenabled"></a>`jobTokenScopeEnabled` | [`Boolean`](#boolean) | Indicates whether CI/CD job tokens generated in this project have restricted access to other projects. |
| <a id="projectcicdsettingkeeplatestartifact"></a>`keepLatestArtifact` | [`Boolean`](#boolean) | Indicates whether the latest artifact should be kept for the project. |
| <a id="projectcicdsettingmergepipelinesenabled"></a>`mergePipelinesEnabled` | [`Boolean`](#boolean) | Indicates whether merged results pipelines are enabled. |
| <a id="projectcicdsettingmergetrainsenabled"></a>`mergeTrainsEnabled` | [`Boolean`](#boolean) | Whether merge trains are enabled. |
| <a id="projectcicdsettingmergetrainsskiptrainallowed"></a>`mergeTrainsSkipTrainAllowed` | [`Boolean!`](#boolean) | Whether merge immediately is allowed for merge trains. |
| <a id="projectcicdsettingpipelinevariablesminimumoverriderole"></a>`pipelineVariablesMinimumOverrideRole` | [`String!`](#string) | Minimum role required to set variables when creating a pipeline or running a job. |
@ -49576,6 +49578,7 @@ see the associated mutation type above.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="aidescriptioncomposerinputdescription"></a>`description` | [`String!`](#string) | Current description. |
| <a id="aidescriptioncomposerinputpreviousresponse"></a>`previousResponse` | [`String`](#string) | Previously AI-generated description content used for context in iterative refinements or follow-up prompts. |
| <a id="aidescriptioncomposerinputresourceid"></a>`resourceId` | [`AiModelID!`](#aimodelid) | Global ID of the resource to mutate. |
| <a id="aidescriptioncomposerinputsourcebranch"></a>`sourceBranch` | [`String`](#string) | Source branch of the changes. |
| <a id="aidescriptioncomposerinputsourceprojectid"></a>`sourceProjectId` | [`ID`](#id) | ID of the project where the changes are from. |

View File

@ -796,16 +796,16 @@ to configure other related settings. These requirements are
| `secret_push_protection_available` | boolean | no | Allow projects to enable secret push protection. This does not enable secret push protection. Ultimate only. |
| `disable_invite_members` | boolean | no | Disable invite members functionality for group. |
### Inactive project settings
### Dormant project settings
You can configure inactive projects deletion or turn it off.
You can configure dormant projects deletion or turn it off.
| Attribute | Type | Required | Description |
|------------------------------------------|------------------|:------------------------------------:|-------------|
| `delete_inactive_projects` | boolean | no | Enable [inactive project deletion](../administration/inactive_project_deletion.md). Default is `false`. [Became operational without feature flag](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) in GitLab 15.4. |
| `inactive_projects_delete_after_months` | integer | no | If `delete_inactive_projects` is `true`, the time (in months) to wait before deleting inactive projects. Default is `2`. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `delete_inactive_projects` | boolean | no | Enable [dormant project deletion](../administration/dormant_project_deletion.md). Default is `false`. [Became operational without feature flag](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) in GitLab 15.4. |
| `inactive_projects_delete_after_months` | integer | no | If `delete_inactive_projects` is `true`, the time (in months) to wait before deleting dormant projects. Default is `2`. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `inactive_projects_min_size_mb` | integer | no | If `delete_inactive_projects` is `true`, the minimum repository size for projects to be checked for inactivity. Default is `0`. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `inactive_projects_send_warning_email_after_months` | integer | no | If `delete_inactive_projects` is `true`, sets the time (in months) to wait before emailing maintainers that the project is scheduled be deleted because it is inactive. Default is `1`. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
| `inactive_projects_send_warning_email_after_months` | integer | no | If `delete_inactive_projects` is `true`, sets the time (in months) to wait before emailing Maintainers that the project is scheduled be deleted because it is dormant. Default is `1`. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0. |
### Housekeeping settings

View File

@ -31,7 +31,7 @@ To instrument an audit event, the following attributes should be provided:
| Attribute | Type | Required? | Description |
|:-------------|:------------------------------------|:----------|:------------------------------------------------------------------|
| `name` | String | false | Action name to be audited. Represents the [type of the event](#event-type-definitions). Used for error tracking |
| `author` | User | true | User who authors the change. Can be an [internal user](../../administration/internal_users.md). For example, [inactive project deletion](../../administration/inactive_project_deletion.md) audit events are authored by `GitLab-Admin-Bot`. |
| `author` | User | true | User who authors the change. Can be an [internal user](../../administration/internal_users.md). For example, [dormant project deletion](../../administration/dormant_project_deletion.md) audit events are authored by `GitLab-Admin-Bot`. |
| `scope` | User, Project, Group, or Instance | true | Scope which the audit event belongs to |
| `target` | Object | true | Target object being audited |
| `message` | String | true | Message describing the action ([not translated](#i18n-and-the-audit-event-message-attribute)) |

View File

@ -140,7 +140,6 @@ Read more about [Gems development guidelines](gems.md).
When upgrading the Rails gem and its dependencies, you also should update the following:
- The [`activerecord_version` in the vendored `attr_encrypted` gemspec](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/gems/attr_encrypted/attr_encrypted.gemspec).
- The [`Gemfile` in the `qa` directory](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/Gemfile).
You should also update npm packages that follow the current version of Rails:

View File

@ -80,19 +80,22 @@ a minimum implementation of `AbstractReferenceFilter` should define:
### Add a new reference prefix and filter
For reference filters for new objects, use a prefix format following the pattern
`^<object_type>#`, because:
For reference filters for new objects, use a format following the pattern
`[object_type:identifier]`, because:
1. Varied single-character prefixes are hard for users to track. Especially for
lower-use object types, this can diminish value for the feature.
1. Suitable single-character prefixes are limited.
1. Suitable single-character prefixes are limited and no longer allowed for new references.
1. Following a consistent pattern allows users to infer the existence of new features.
To add a reference prefix for a new object `apple`,which has both a name and ID,
The [Extensible reference filters](https://gitlab.com/groups/gitlab-org/-/epics/7563)
epic discusses the use of this format.
To add a reference prefix for a new object `apple`, which has both a name and ID,
format the reference as:
- `^apple#123` for identification by ID.
- `^apple#"Granny Smith"` for identification by name.
- `[apple:123]` for identification by ID.
- `[apple:"Granny Smith"]` for identification by name.
### Performance

View File

@ -20,8 +20,6 @@ We strive to run GitLab using the latest Rails releases to benefit from performa
1. For major and minor version updates, run `bin/rails app:update` and check if any of the suggested changes should be applied.
1. Update the `activesupport` version in `qa/Gemfile`.
1. Run `bundle update --conservative activesupport` in the `qa` folder.
1. Update the `activerecord_version` version in `vendor/gems/attr_encrypted/attr_encrypted.gemspec`.
1. Run `bundle update --conservative activerecord` in the `vendor/gems/attr_encrypted` folder.
1. Run `find gems -name Gemfile -exec bundle update --gemfile {} activesupport --patch --conservative \;` and replace `--patch` in the command with `--minor` or `--major` as needed.
1. Resolve any Bundler conflicts.
1. Ensure that `@rails/ujs` and `@rails/actioncable` npm packages match the new rails version in [`package.json`](https://gitlab.com/gitlab-org/gitlab/blob/master/package.json).

View File

@ -58,7 +58,7 @@ default keyboard shortcuts to avoid conflicts with your existing Visual Studio c
| Command name | Default keyboard shortcut | Description |
|---------------------------------------|---------------------------------------------|-------------|
| `GitLab.ToggleCodeSuggestions` | None | Turn on or turn off Code Suggestions. |
| `GitLab.OpenDuoChat` | None | Open Duo Chat. |
| `GitLab.OpenDuoChat` | None | Open GitLab Duo Chat. |
| `GitLab.GitLabDuoNextSuggestions` | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>N</kbd> | Switch to the next code suggestion. |
| `GitLab.GitLabDuoPreviousSuggestions` | None | Switch to the previous code suggestion. |
| `GitLab.GitLabExplainTerminalWithDuo` | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>E</kbd> | Explain selected text in the terminal. |

View File

@ -93,12 +93,12 @@ For more information about simplifying this process, see
[issue 577](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/issues/577)
in the `gitlab-vscode-extension` project.
## Known issue: Duo Chat fails to initialize in remote environments
## Known issue: GitLab Duo Chat fails to initialize in remote environments
When using GitLab Duo Chat in remote development environments (such as browser-based VS Code or remote
SSH connections), you might encounter initialization failures like:
- Blank or non-loading Duo Chat panel.
- Blank or non-loading Chat panel.
- Errors in logs: `The webview didn't initialize in 10000ms`.
- Extension attempting to connect to inaccessible local URLs.

View File

@ -579,7 +579,7 @@ For details on saving and transporting Docker images as a file, see the Docker d
```
1. If your local Docker container registry is running securely over `HTTPS`, but you're using a
self-signed certificate, then you must set `CS_DOCKER_INSECURE: "true"` in the above
self-signed certificate, then you must set `CS_DOCKER_INSECURE: "true"` in the
`container_scanning` section of your `.gitlab-ci.yml`.
#### Automating container scanning vulnerability database updates with a pipeline
@ -606,7 +606,7 @@ update-scanner-image:
- docker push $TARGET_IMAGE
```
The above template works for a GitLab Docker registry running on a local installation. However, if
The previous template works for a GitLab Docker registry running on a local installation. However, if
you're using a non-GitLab Docker registry, you must change the `$CI_REGISTRY` value and the
`docker login` credentials to match your local registry's details.
@ -663,7 +663,7 @@ mirror trivy java db:
The vulnerability database is not a regular Docker image, so it is not possible to pull it by using `docker pull`.
The image shows an error if you go to it in the GitLab UI.
If the above container registry is `gitlab.example.com/trivy-java-db-mirror`, then the container scanning job should be configured in the following way. Do not add the tag `:1` at the end, it is added by `trivy`:
If the container registry is `gitlab.example.com/trivy-java-db-mirror`, then the container scanning job should be configured in the following way. Do not add the tag `:1` at the end, it is added by `trivy`:
```yaml
include:

View File

@ -22,10 +22,10 @@ It is recommended that a two cookie based approach is taken, as outlined in the
of the RFC.
If the application is using a common framework, there is a chance that Anti-CSRF protection
is built in but needs to be enabled. Consult your application framework documentation for
is built-in but needs to be enabled. Consult your application framework documentation for
details.
If neither of the above are applicable, it is **strongly** recommended that a third party library is used.
If neither of these options are applicable, it is **strongly** recommended that a third party library is used.
Implementing a secure Anti-CSRF system is a significant investment and difficult to do correctly.
## Details

View File

@ -94,8 +94,8 @@ hosted within your network.
## Specific scanner instructions
Each individual scanner may be slightly different than the steps described
above. You can find more information at each of the pages below:
Each individual scanner may be slightly different than the steps previously described.
You can find more information at each of the pages below:
- [Container scanning offline directions](../container_scanning/_index.md#running-container-scanning-in-an-offline-environment)
- [SAST offline directions](../sast/_index.md#running-sast-in-an-offline-environment)
@ -184,7 +184,7 @@ template:
### Alternate way without the official template
If it's not possible to follow the above method, the images can be transferred manually instead:
If it's not possible to follow the previous method, the images can be transferred manually instead:
#### Example image packager script
@ -235,12 +235,12 @@ these steps:
1. Load the container images into the local registry. GitLab Secure leverages analyzer container
images to do the various scans. These images must be available as part of running AutoDevOps.
Before running AutoDevOps, follow the [above steps](#using-the-official-gitlab-template)
Before running AutoDevOps, follow the steps in the [official GitLab template](#using-the-official-gitlab-template)
to load those container images into the local container registry.
1. Set the CI/CD variable to ensure that AutoDevOps looks in the right place for those images.
The AutoDevOps templates leverage the `SECURE_ANALYZERS_PREFIX` variable to identify the location
of analyzer images. This variable is discussed above in [Using the secure bundle created](#using-the-secure-bundle-created).
of analyzer images. For more information about this see [Using the secure bundle created](#using-the-secure-bundle-created).
Ensure that you set this variable to the correct value for where you loaded the analyzer images.
You could consider doing this with a project CI/CD variable or by [modifying](../../../topics/autodevops/customize.md#customize-gitlab-ciyml)
the `.gitlab-ci.yml` file directly.

View File

@ -143,7 +143,7 @@ pipelines, each of which may contain a security scan.
If a project uses [merge request pipelines](../../../ci/pipelines/merge_request_pipelines.md), you must set the CI/CD variable `AST_ENABLE_MR_PIPELINES` to `"true"` for the security scanning jobs to be present in the pipeline.
For more information see [Use security scanning tools with merge request pipelines](../detect/roll_out_security_scanning.md#use-security-scanning-tools-with-merge-request-pipelines).
For projects where many pipelines have run on the latest commit (for example, inactive projects), policy evaluation considers a maximum of 1,000 pipelines from both the source and target branches of the merge request.
For projects where many pipelines have run on the latest commit (for example, dormant projects), policy evaluation considers a maximum of 1,000 pipelines from both the source and target branches of the merge request.
For parent-child pipelines, policy evaluation considers a maximum of 1,000 child pipelines.

View File

@ -239,7 +239,7 @@ If your scheduled pipelines are not running as expected, follow these troublesho
- Ensure the configuration includes appropriate workflow rules for scheduled pipelines.
1. **Verify policy configuration**:
- Ensure the policy is enabled (`enabled: true`).
- Verify taht the schedule configuration has the correct format and valid values.
- Verify that the schedule configuration has the correct format and valid values.
- Verify that the time zone setting is correct (if specified).
1. **Review logs and activity**:
- Check the security policy project's CI/CD pipeline logs for any errors.

View File

@ -175,7 +175,7 @@ To enable GitLab Advanced SAST by using the pipeline editor:
- In GitLab 17.1, you must manually copy the contents of the GitLab Advanced SAST job into your CI/CD pipeline definition.
- Set the CI/CD variable `GITLAB_ADVANCED_SAST_ENABLED` to `true`.
See the [minimal YAML example above](#edit-the-cicd-pipeline-definition-manually).
See the [minimal YAML example](#edit-the-cicd-pipeline-definition-manually).
1. Select the **Validate** tab, then select **Validate pipeline**.
The message **Simulation completed successfully** confirms the file is valid.

View File

@ -256,7 +256,7 @@ variables:
SECRET_DETECTION_RULESET_GIT_REFERENCE: "gitlab.com/example-group/remote-ruleset-project"
```
Pipeline secret detection assumes the configuration is defined in `.gitlab/secret-detection-ruleset.toml` file in the repository referenced by the CI variable where the remote ruleset is stored. If that file doesn't exist, make sure to [create one](#create-a-ruleset-configuration-file) and follow the steps to [override](#override-a-rule) or [disable](#disable-a-rule) a predefined rule as outlined above.
Pipeline secret detection assumes the configuration is defined in `.gitlab/secret-detection-ruleset.toml` file in the repository referenced by the CI variable where the remote ruleset is stored. If that file doesn't exist, make sure to [create one](#create-a-ruleset-configuration-file) and follow the steps to [override](#override-a-rule) or [disable](#disable-a-rule) a predefined rule as previously outlined.
{{< alert type="note" >}}
@ -316,7 +316,7 @@ regex = '''Custom Raw Ruleset T[est]{3}'''
"""
```
The above example replaces the default ruleset with a rule that checks for the regex defined - `Custom Raw Ruleset T` with a suffix of 3 characters from either one of `e`, `s`, or `t` letters.
The previous example replaces the default ruleset with a rule that checks for the regex defined - `Custom Raw Ruleset T` with a suffix of 3 characters from either one of `e`, `s`, or `t` letters.
For more information on the passthrough syntax to use, see [Schema](custom_rulesets_schema.md#schema).
@ -473,7 +473,7 @@ To do that with a `git` passthrough, add the following to `.gitlab/secret-detect
Pipeline secret detection assumes the remote ruleset configuration file is called `gitleaks.toml`, and is stored in `config` directory on the `main` branch of the referenced repository.
To extend the default ruleset, the `gitleaks.toml` file should use `[extend]` directive similar to the example above:
To extend the default ruleset, the `gitleaks.toml` file should use `[extend]` directive similar to the previous example:
```toml
# https://gitlab.com/user_group/central_repository_with_shared_ruleset/-/raw/main/config/gitleaks.toml

View File

@ -144,7 +144,7 @@ spotbugs-sast:
- merge_requests
```
To transition the above configuration to the new `rules` syntax, the override
To transition the previous configuration to the new `rules` syntax, the override
would be written as follows:
```yaml

View File

@ -62,7 +62,7 @@ can be one of the following:
| [CVSS v3.1 Qualitative Severity Rating](https://www.first.org/cvss/v3.1/specification-document#Qualitative-Severity-Rating-Scale) | `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H` |
To provide consistent vulnerability severity level values, the GitLab vulnerability analyzers
convert from the above values to a standardized GitLab vulnerability severity level, as outlined in
convert from the previous values to a standardized GitLab vulnerability severity level, as outlined in
the following tables:
## Container Scanning

View File

@ -206,7 +206,7 @@ You can leave it out if your compliance pipeline only ever runs in labeled proje
#### Compliance pipelines and custom pipeline configuration hosted externally
The example above assumes that all projects host their pipeline configuration in the same project.
The previous example assumes that all projects host their pipeline configuration in the same project.
If any projects use [configuration hosted externally](../../ci/pipelines/settings.md#specify-a-custom-cicd-configuration-file),
the example configuration does not work. See [issue 393960](https://gitlab.com/gitlab-org/gitlab/-/issues/393960)
for more details.
@ -242,7 +242,7 @@ With projects that use externally hosted configuration, you can try the this wor
#### Compliance pipelines in merge requests originating in project forks
When a merge request originates in a fork, the branch to be merged usually only exists in the fork.
When creating such a merge request against a project with compliance pipelines, the above snippet fails with a
When creating such a merge request against a project with compliance pipelines, the previous snippet fails with a
`Project <project-name> reference <branch-name> does not exist!` error message.
This error occurs because in the context of the target project, `$CI_COMMIT_REF_NAME` evaluates to a non-existing
branch name.
@ -270,7 +270,7 @@ include: # Execute individual project's configuration (if project contains .git
#### Compliance pipelines in projects with no configuration file
The [example configuration](#example-configuration) above assumes that all projects contain
The [example configuration](#example-configuration) assumes that all projects contain
a pipeline configuration file (`.gitlab-ci.yml` by default). However, in projects
with no configuration file (and therefore no pipelines by default), the compliance pipeline
fails because the file specified in `include:project` is required.
@ -295,7 +295,7 @@ In this example, a configuration file is only included if it exists for the give
in the project in `exists:project: $CI_PROJECT_PATH'`.
If `exists:project` is not specified in the compliance pipeline configuration, it searches for files in the project
in which the `include` is defined. In compliance pipelines, the `include` from the example above
in which the `include` is defined. In compliance pipelines, the `include` from the previous example
is defined in the project hosting the compliance pipeline configuration file, not the project
running the pipeline.

View File

@ -420,9 +420,9 @@ Projects are permanently deleted after a seven-day delay.
See how to [view and restore projects marked for deletion](../project/working_with_projects.md#restore-a-project).
### Inactive project deletion
### Dormant project deletion
[Inactive project deletion](../../administration/inactive_project_deletion.md) is disabled on GitLab.com.
[Dormant project deletion](../../administration/dormant_project_deletion.md) is disabled on GitLab.com.
## Package registry limits

View File

@ -127,7 +127,7 @@ To display the deploy boards for a specific [environment](../../ci/environments/
![deploy boards Kubernetes Label](img/deploy_boards_kubernetes_label_v11_9.png)
Once all of the above are set up and the pipeline has run at least once,
Once all of these previous instructions are set up and the pipeline has run at least once,
go to the environments page under **Operate > Environments**.
Deploy boards are visible by default. You can explicitly select

View File

@ -20,6 +20,7 @@ module API
use AdminModeMiddleware
use ResponseCoercerMiddleware
use TrackAPIRequestFromRunnerMiddleware
helpers HelperMethods
@ -276,6 +277,63 @@ module API
nil
end
end
class TrackAPIRequestFromRunnerMiddleware < ::Grape::Middleware::Base
delegate :request, :endpoint_id, :current_user, :current_token, to: :context
def after
return unless success? && current_project && current_token
return unless Feature.enabled?(:track_api_request_from_runner, current_project)
return unless request_from_runner?
::Gitlab::InternalEvents.track_event(
'api_request_from_runner',
project: current_project,
additional_properties: {
label: endpoint_id,
property: token_type,
cross_project_request: cross_project_request?.to_s
}
)
# Explicit nil is needed or the api call return value will be overwritten
nil
end
private
def current_project
project = context.instance_variable_get(:@project)
return unless project.is_a?(Project)
project
end
def success?
return unless @app_response
(200..299).cover?(@app_response[0])
end
def request_from_runner?
return unless request
::Ci::RunnerManager.ip_address_exists?(request.ip)
end
def token_type
return unless current_token
::Authn::AgnosticTokenIdentifier.name(current_token.to_s)
end
def cross_project_request?
return unless current_project
return unless current_user&.from_ci_job_token?
!current_user.ci_job_token_scope.self_referential?(current_project)
end
end
end
end

View File

@ -31,6 +31,7 @@ module API
bad_request!('Found more than one set of credentials') if found.size > 1
location, raw = found.first
set_current_token(raw)
find_token_from_raw_credentials(strategies[location], raw)
end
@ -59,6 +60,10 @@ module API
private
def set_current_token(raw)
self.current_token = raw.password
end
def find_token_from_raw_credentials(token_types, raw)
token_types.each do |token_type|
# Resolve a token from the raw credentials

View File

@ -4,6 +4,15 @@ module API
module Helpers
module Packages
module Nuget
extend ::Gitlab::Utils::Override
override :render_api_error!
def render_api_error!(message, status)
header('X-NuGet-Warning', "GitLab: #{message}") if message.present?
super
end
def find_packages
packages = package_finder(params[:package_name]).execute

View File

@ -29,5 +29,13 @@ module Authn
def self.token_type(plaintext)
TOKEN_TYPES.find { |x| x.prefix?(plaintext) }
end
def self.name(plaintext)
type = token_type(plaintext)
type = type.new(plaintext, nil).resource_name if type == ::Authn::Tokens::PersonalAccessToken
return unless type
type.to_s.demodulize.underscore
end
end
end

View File

@ -32,6 +32,13 @@ module Authn
service.execute
end
def resource_name
return revocable.class.name if revocable.user.human?
return unless resource
"#{resource.class.name}AccessToken"
end
private
attr_reader :current_user

View File

@ -49,6 +49,8 @@ module Gitlab
DEPLOY_TOKEN_HEADER
].freeze
attr_accessor :current_token
# Check the Rails session for valid authentication details
def find_user_from_warden
current_request.env['warden']&.authenticate if verified_request?
@ -148,13 +150,13 @@ module Gitlab
return unless route_authentication_setting[:deploy_token_allowed]
return unless Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
self.current_token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
if has_basic_credentials?(current_request)
_, token = user_name_and_password(current_request)
_, self.current_token = user_name_and_password(current_request)
end
deploy_token = DeployToken.active.find_by_token(token.to_s)
deploy_token = DeployToken.active.find_by_token(current_token.to_s)
@current_authenticated_deploy_token = deploy_token # rubocop:disable Gitlab/ModuleWithInstanceVariables
deploy_token
@ -167,15 +169,15 @@ module Gitlab
# agents, `Gitlab-Agentk-Api-Request`. Both must be supported until KAS has
# been updated to use the new header, and then this first lookup can be removed.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/406582.
token, _ = if current_request.authorization.present?
token_and_options(current_request)
else
current_request.headers[Gitlab::Kas::INTERNAL_API_AGENTK_REQUEST_HEADER]
end
self.current_token, _ = if current_request.authorization.present?
token_and_options(current_request)
else
current_request.headers[Gitlab::Kas::INTERNAL_API_AGENTK_REQUEST_HEADER]
end
return unless token.present?
return unless current_token.present?
::Clusters::AgentToken.active.find_by_token(token.to_s)
::Clusters::AgentToken.active.find_by_token(current_token.to_s)
end
def find_runner_from_token
@ -259,10 +261,10 @@ module Gitlab
def find_user_from_job_bearer_token
return unless route_authentication_setting[:job_token_allowed]
token = parsed_oauth_token
return unless token
self.current_token = parsed_oauth_token
return unless current_token
job = ::Ci::AuthJobFinder.new(token: token).execute
job = ::Ci::AuthJobFinder.new(token: current_token).execute
return unless job
@current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
@ -300,24 +302,24 @@ module Gitlab
end
def find_personal_access_token
token = extract_personal_access_token
return unless token
self.current_token = extract_personal_access_token
return unless current_token
# The runner sends the job token for PUT /api/jobs/:id in the PRIVATE-TOKEN header
# and the token JSON parameter. Ignore this personal access token so
# that the job token can be authenticated.
return if api_request? && token.start_with?(::Ci::Build::TOKEN_PREFIX)
return if api_request? && current_token.start_with?(::Ci::Build::TOKEN_PREFIX)
# Expiration, revocation and scopes are verified in `validate_access_token!`
PersonalAccessToken.find_by_token(token.to_s) || raise(UnauthorizedError)
PersonalAccessToken.find_by_token(current_token.to_s) || raise(UnauthorizedError)
end
def find_oauth_access_token
token = parsed_oauth_token
return unless token
self.current_token = parsed_oauth_token
return unless current_token
# Expiration, revocation and scopes are verified in `validate_access_token!`
oauth_token = OauthAccessToken.by_token(token)
oauth_token = OauthAccessToken.by_token(current_token)
raise UnauthorizedError unless oauth_token
oauth_token.revoke_previous_refresh_token!
@ -333,8 +335,8 @@ module Gitlab
return unless route_authentication_setting[:basic_auth_personal_access_token]
return unless has_basic_credentials?(current_request)
_username, password = user_name_and_password(current_request)
PersonalAccessToken.find_by_token(password.to_s)
_username, self.current_token = user_name_and_password(current_request)
PersonalAccessToken.find_by_token(current_token.to_s)
end
def find_feed_token_user(token)
@ -373,12 +375,12 @@ module Gitlab
end
def find_user_from_job_token_query_params_or_header
token = current_request.params[JOB_TOKEN_PARAM].presence ||
self.current_token = current_request.params[JOB_TOKEN_PARAM].presence ||
current_request.params[RUNNER_JOB_TOKEN_PARAM].presence ||
current_request.env[JOB_TOKEN_HEADER].presence
return unless token
return unless current_token
job = find_valid_running_job_by_token!(token.to_s)
job = find_valid_running_job_by_token!(current_token.to_s)
@current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
job.user
@ -387,11 +389,11 @@ module Gitlab
def find_user_from_job_token_basic_auth
return unless has_basic_credentials?(current_request)
login, password = user_name_and_password(current_request)
return unless login.present? && password.present?
login, self.current_token = user_name_and_password(current_request)
return unless login.present? && current_token.present?
return unless ::Gitlab::Auth::CI_JOB_USER == login
job = find_valid_running_job_by_token!(password.to_s)
job = find_valid_running_job_by_token!(current_token.to_s)
@current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables
job.user

View File

@ -4,6 +4,7 @@ module Gitlab
module Database
MAIN_DATABASE_NAME = 'main'
CI_DATABASE_NAME = 'ci'
SEC_DATABASE_NAME = 'sec'
DEFAULT_POOL_HEADROOM = 10
# This constant is used when renaming tables concurrently.

View File

@ -11,7 +11,7 @@ ARG QA_BUILD_TARGET=ee
ARG RUBY_VERSION=3.3.7
ARG RUST_VERSION=1.73
FROM registry.gitlab.com/gitlab-org/gitlab-build-images/${BUILD_OS}-${OS_VERSION}-ruby-${RUBY_VERSION}-rust-${RUST_VERSION}:git-${GIT_VERSION}-lfs-${LFS_VERSION}-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-${GCLOUD_VERSION}-kubectl-${KUBECTL_VERSION}-helm-${HELM_VERSION} AS foss
FROM registry.gitlab.com/gitlab-org/gitlab-build-images/${BUILD_OS}-${OS_VERSION}-ruby-${RUBY_VERSION}:git-${GIT_VERSION}-lfs-${LFS_VERSION}-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-${GCLOUD_VERSION}-kubectl-${KUBECTL_VERSION}-helm-${HELM_VERSION} AS foss
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive"

View File

@ -375,7 +375,7 @@ class ApplicationSettingsAnalysis
DOC_API_SETTINGS_FILE_PATH = File.expand_path('../../doc/api/settings.md', __dir__)
DOC_API_SETTINGS_TABLE_REGEX = Regexp.new(
"## Available settings(?:.*?)(?:--\|\n)+?(?<rows>.+)" \
"### Inactive project settings", Regexp::MULTILINE
"### Dormant project settings", Regexp::MULTILINE
)
DOC_PAGE_HEADERS = [

View File

@ -253,7 +253,9 @@ RSpec.describe 'Database schema',
subscription_user_add_on_assignment_versions: %w[item_id user_id purchase_id], # Managed by paper_trail gem, no need for FK on the historical data
virtual_registries_packages_maven_cache_entries: %w[group_id], # We can't use a foreign key due to object storage references
# system_defined_status_id reference to fixed items model which is stored in code
work_item_current_statuses: %w[system_defined_status_id]
work_item_current_statuses: %w[system_defined_status_id],
# we can't use a foreign key reference because we want to preserve namespace_id for asynchronous deletion
p_knowledge_graph_replicas: %w[namespace_id]
}.with_indifferent_access.freeze
end

View File

@ -530,6 +530,6 @@ FactoryBot.define do
trait :instance do
project { nil }
instance { true }
organization { association(:organization, :default) }
organization { association(:organization) }
end
end

View File

@ -209,8 +209,8 @@
- "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143433\n" # Submit MR URL
- "instrumentation" # Filters to the analytics instrumentation group
- "\n" # Accept analytics:monitor:analytics_instrumentation
- "se " # Select product category: service_ping
- "tt \n" # Select additional product category: settings
- "service_ " # Select product category: service_ping
- "d \n" # Select additional product category: service_desk
- "1\n" # Select: [free, premium, ultimate]
- "y\n" # Create file
- "4\n" # Exit
@ -257,8 +257,8 @@
- "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143433\n" # Submit MR URL
- "instrumentation" # Filters to the analytics instrumentation group
- "\n" # Accept analytics:monitor:analytics_instrumentation
- "se " # Select product category: service_ping
- "tt \n" # Select additional product category: settings
- "service_ " # Select product category: service_ping
- "d \n" # Select additional product category: service_desk
- "1\n" # Select: [free, premium, ultimate]
- "y\n" # Create file
- "1\n" # Create another event

View File

@ -8,8 +8,8 @@ identifiers:
- user
product_group: analytics_instrumentation
product_categories:
- service_desk
- service_ping
- settings
milestone: '16.6'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143433
tiers:

View File

@ -109,7 +109,7 @@ describe('UserMenu component', () => {
describe('User status item', () => {
let item;
const setItem = async ({
const setItem = ({
can_update: canUpdate = false,
busy = false,
customized = false,
@ -119,13 +119,6 @@ describe('UserMenu component', () => {
{ status: { ...userMenuMockStatus, can_update: canUpdate, busy, customized } },
stubs,
);
// Mock mounting the modal if we can update
if (canUpdate) {
expect(wrapper.vm.setStatusModalReady).toEqual(false);
findSetStatusModal().vm.$emit('mounted');
await nextTick();
expect(wrapper.vm.setStatusModalReady).toEqual(true);
}
item = wrapper.findByTestId('status-item');
};
@ -199,11 +192,14 @@ describe('UserMenu component', () => {
${true} | ${false}
${false} | ${true}
`('and the status is busy or customized', ({ busy, customized }) => {
it('should pass the current status to the modal', () => {
it('should pass the current status to the modal', async () => {
createWrapper({
status: { ...userMenuMockStatus, can_update: true, busy, customized },
});
wrapper.findByTestId('status-item').vm.$emit('action');
await nextTick();
expect(findSetStatusModal().exists()).toBe(true);
expect(findSetStatusModal().props()).toMatchObject({
defaultEmoji: 'speech_balloon',
@ -214,11 +210,14 @@ describe('UserMenu component', () => {
});
});
it('casts falsey values to empty strings', () => {
it('casts falsey values to empty strings', async () => {
createWrapper({
status: { can_update: true, busy, customized },
});
wrapper.findByTestId('status-item').vm.$emit('action');
await nextTick();
expect(findSetStatusModal().exists()).toBe(true);
expect(findSetStatusModal().props()).toMatchObject({
defaultEmoji: 'speech_balloon',
@ -231,11 +230,14 @@ describe('UserMenu component', () => {
});
describe('and the status is neither busy nor customized', () => {
it('should pass an empty status to the modal', () => {
it('should pass an empty status to the modal', async () => {
createWrapper({
status: { ...userMenuMockStatus, can_update: true, busy: false, customized: false },
});
wrapper.findByTestId('status-item').vm.$emit('action');
await nextTick();
expect(findSetStatusModal().exists()).toBe(true);
expect(findSetStatusModal().props()).toMatchObject({
defaultEmoji: 'speech_balloon',

View File

@ -7,7 +7,7 @@ RSpec.describe Types::Ci::CiCdSettingType, feature_category: :continuous_integra
it 'exposes the expected fields' do
expected_fields = %w[
inbound_job_token_scope_enabled job_token_scope_enabled
group_runners_enabled inbound_job_token_scope_enabled job_token_scope_enabled
keep_latest_artifact merge_pipelines_enabled project
push_repository_for_job_token_allowed
pipeline_variables_minimum_override_role

View File

@ -38,6 +38,8 @@ RSpec.describe API::Helpers::Authentication do
end
end
cls.attr_writer :current_token
cls.define_method(:unauthorized!) { raise '401' }
cls.define_method(:bad_request!) { |m| raise "400 - #{m}" }
@ -107,6 +109,12 @@ RSpec.describe API::Helpers::Authentication do
it 'returns the token' do
expect(subject).to be(token)
end
it 'sets the current_token' do
expect(object).to receive(:current_token=).with(token.password)
subject
end
end
shared_examples 'an unauthorized request' do
@ -127,10 +135,10 @@ RSpec.describe API::Helpers::Authentication do
end
context 'with one set of located credentials' do
let(:locators) { [double(extract: true)] }
let(:locators) { [double(extract: token)] }
let(:token) { double(password: 'secret') }
context 'when the credentials contain a valid token' do
let(:token) { double }
let(:resolvers) { [double(resolve: token)] }
it_behaves_like 'an authenticated request'
@ -151,7 +159,7 @@ RSpec.describe API::Helpers::Authentication do
end
context 'when a resolver raises UnauthorizedError' do
let(:locators) { [double(extract: true)] }
let(:locators) { [double(extract: double(password: nil))] }
let(:resolvers) do
r = double
expect(r).to receive(:resolve).and_raise(Gitlab::Auth::UnauthorizedError)

View File

@ -100,4 +100,40 @@ RSpec.describe Authn::AgnosticTokenIdentifier, feature_category: :system_access
end
end
end
describe '.name' do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:project) { create(:project, developers: project_bot) }
let_it_be(:project_access_token) { create(:personal_access_token, user: project_bot).token }
let_it_be(:group_bot) { create(:user, :project_bot) }
let_it_be(:group) { create(:group, developers: group_bot) }
let_it_be(:group_access_token) { create(:personal_access_token, user: group_bot).token }
let_it_be(:nil_resource_access_token) { create(:personal_access_token, user: create(:user, :project_bot)).token }
let_it_be(:ci_job_token) { create(:ci_build).token }
subject { described_class.name(plaintext) }
where(:plaintext, :name) do
ref(:personal_access_token) | 'personal_access_token'
ref(:project_access_token) | 'project_access_token'
ref(:group_access_token) | 'group_access_token'
ref(:nil_resource_access_token) | nil
ref(:impersonation_token) | 'personal_access_token'
ref(:feed_token) | 'feed_token'
ref(:deploy_token) | 'deploy_token'
ref(:oauth_application_secret) | 'oauth_application_secret'
ref(:cluster_agent_token) | 'cluster_agent_token'
ref(:runner_authentication_token) | 'runner_authentication_token'
ref(:ci_trigger_token) | 'ci_trigger_token'
ref(:feature_flags_client_token) | 'feature_flags_client_token'
ref(:gitlab_session) | 'gitlab_session'
ref(:incoming_email_token) | 'incoming_email_token'
ref(:ci_job_token) | 'ci_job_token'
'unsupported' | nil
end
with_them do
it { is_expected.to eq name }
end
end
end

View File

@ -41,6 +41,12 @@ RSpec.describe Authn::Tokens::PersonalAccessToken, feature_category: :system_acc
expect(token.revoke!(admin).status).to eq(:success)
end
end
describe '#resource_name' do
subject(:resource_name) { token.resource_name }
it { is_expected.to eq 'PersonalAccessToken' }
end
end
context 'when the token is a resource token' do
@ -52,6 +58,12 @@ RSpec.describe Authn::Tokens::PersonalAccessToken, feature_category: :system_acc
it 'successfully revokes the token', :enable_admin_mode do
expect(token.revoke!(admin).status).to eq(:success)
end
describe '#resource_name' do
subject(:resource_name) { token.resource_name }
it { is_expected.to eq 'ProjectAccessToken' }
end
end
context 'when the token is a group access token' do
@ -62,6 +74,22 @@ RSpec.describe Authn::Tokens::PersonalAccessToken, feature_category: :system_acc
it 'successfully revokes the token', :enable_admin_mode do
expect(token.revoke!(admin).status).to eq(:success)
end
describe '#resource_name' do
subject(:resource_name) { token.resource_name }
it { is_expected.to eq 'GroupAccessToken' }
end
end
end
context 'when the token is from a bot without a resource' do
let_it_be(:plaintext) { create(:personal_access_token, user: create(:user, :project_bot)).token }
describe '#resource_name' do
subject(:resource_name) { token.resource_name }
it { is_expected.to be_nil }
end
end

View File

@ -201,6 +201,27 @@ RSpec.describe Ci::RunnerManager, feature_category: :fleet_visibility, type: :mo
include_examples 'runner with status scope'
end
describe '.ip_address_exists?' do
let(:existing_ip_address) { '127.0.0.1' }
let(:ip_address_to_find) { existing_ip_address }
subject { described_class.ip_address_exists?(ip_address_to_find) }
before do
create(:ci_runner_machine, ip_address: existing_ip_address)
end
context 'when the ip address exists' do
it { is_expected.to be(true) }
end
context 'when the ip address does not exist' do
let(:ip_address_to_find) { '10.0.0.1' }
it { is_expected.to be(false) }
end
end
describe '.available_statuses' do
subject { described_class.available_statuses }

View File

@ -0,0 +1,260 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::APIGuard::TrackAPIRequestFromRunnerMiddleware, :request_store, feature_category: :permissions do
using RSpec::Parameterized::TableSyntax
RSpec.shared_examples 'event tracking' do
let(:all_metrics) do
[
"redis_hll_counters.count_#{property}s_from_api_request_from_runner_weekly",
"redis_hll_counters.count_#{property}s_from_api_request_from_runner_monthly"
]
end
it 'logs to Snowplow, Redis, and product analytics tooling',
:clean_gitlab_redis_shared_state, :aggregate_failures do
expected_attributes = {
project: project,
namespace: project.namespace,
category: 'InternalEventTracking',
feature_enabled_by_namespace_ids: nil,
additional_properties: {
**additional_properties,
**{
label: label,
property: property
}.compact
}
}
expect { subject }
.to trigger_internal_events(event)
.with(expected_attributes)
.and increment_usage_metrics(*all_metrics)
end
end
RSpec.shared_examples 'event not tracked' do
it 'does not record an internal event' do
expect(Gitlab::InternalEvents).not_to receive(:track_event).with(event, any_args)
subject
end
end
it 'is loaded' do
expect(API::API.middleware).to include([:use, described_class])
end
describe '#after' do
context 'when requesting an endpoint from a runner' do
let_it_be(:token_user) { create(:user) }
let_it_be(:token_user_project) { create(:project, developers: token_user) }
let_it_be(:status) { 200 }
let_it_be(:personal_access_token) { create(:personal_access_token, user: token_user).token }
let_it_be(:deploy_token) { create(:deploy_token).token }
let_it_be(:ci_job_token) { create(:ci_build, :running, user: token_user, project: token_user_project).token }
let_it_be(:token) { personal_access_token }
let_it_be(:token_param) { :private_token }
let_it_be(:endpoint) { '/test' }
let_it_be(:ip_address) { '10.0.0.1' }
let!(:runner_machine) { create(:ci_runner_machine, ip_address: ip_address) }
let_it_be(:event) { 'api_request_from_runner' }
let_it_be(:project) { token_user_project }
let_it_be(:label) { "GET /api/:version#{endpoint}" }
let_it_be(:property) { 'personal_access_token' }
let_it_be(:additional_properties) { { cross_project_request: '' } }
let_it_be(:app) do
Class.new(API::API).tap do |app|
app.route_setting :authentication, job_token_allowed: true
app.get endpoint do
user_project
status params[:status].to_i
end
end
end
subject(:request) do
get api(endpoint),
params: { id: token_user_project&.id, token_param => token, status: status },
headers: { REMOTE_ADDR: ip_address }
end
it_behaves_like 'event tracking'
context 'when the endpoint return status is not in the 200 range' do
let(:status) { 500 }
it_behaves_like 'event not tracked'
end
context 'when there is no project available' do
let(:token_user_project) { nil }
it_behaves_like 'event not tracked'
end
context 'when there is no token available' do
let(:token) { nil }
it_behaves_like 'event not tracked'
end
context 'when the `track_api_request_from_runner` feature flag is disabled' do
before do
stub_feature_flags(track_api_request_from_runner: false)
end
it_behaves_like 'event not tracked'
end
context 'when no runner machines exist with the remote ip address' do
let(:runner_machine) { nil }
it_behaves_like 'event not tracked'
end
context 'with different token types' do
let_it_be(:project_bot) { create(:user, :project_bot) }
let_it_be(:project_bot_project) { create(:project, developers: project_bot) }
let_it_be(:project_access_token) { create(:personal_access_token, user: project_bot).token }
let_it_be(:group_bot) { create(:user, :project_bot) }
let_it_be(:group_bot_group) { create(:group, developers: group_bot) }
let_it_be(:group_access_token) { create(:personal_access_token, user: group_bot).token }
let_it_be(:oauth_application_secret) do
create(:oauth_access_token, scopes: [:api]).tap do |oauth_access_token|
oauth_access_token.update_column(:token, oauth_access_token.application.secret)
end.application.plaintext_secret
end
let_it_be(:app) do
Class.new(API::API).tap do |app|
app.route_setting :authentication, deploy_token_allowed: true
app.get endpoint do
@project = Project.find(params[:id])
status find_user_from_sources ? 200 : 401
end
end
end
where(:token, :token_param, :token_header, :token_type) do
ref(:personal_access_token) | :private_token | nil | 'personal_access_token'
ref(:project_access_token) | :private_token | nil | 'project_access_token'
ref(:group_access_token) | :private_token | nil | 'group_access_token'
ref(:deploy_token) | nil | 'HTTP_DEPLOY_TOKEN' | 'deploy_token'
ref(:oauth_application_secret) | :access_token | nil | 'oauth_application_secret'
end
with_them do
subject(:request) do
get api(endpoint),
params: { id: token_user_project.id, token_param => token },
headers: { REMOTE_ADDR: ip_address, token_header => token }
end
it_behaves_like 'event tracking' do
let(:property) { token_type }
end
end
context 'with cluster agent token' do
let_it_be(:cluster_agent_token) { create(:cluster_agent_token, token_encrypted: nil).token }
let_it_be(:app) do
Class.new(API::API).tap do |app|
app.helpers ::API::Helpers::Kubernetes::AgentHelpers
app.route_setting :authentication, cluster_agent_token_allowed: true
app.get endpoint do
@project = Project.find(params[:id])
status agent_token ? 200 : 401
end
end
end
subject(:request) do
get api(endpoint),
params: { id: token_user_project.id },
headers: { REMOTE_ADDR: ip_address, 'Gitlab-Agentk-Api-Request' => cluster_agent_token }
end
it_behaves_like 'event tracking' do
let(:property) { 'cluster_agent_token' }
end
end
end
context 'with token from namespace inheritable' do
let_it_be(:app) do
Class.new(API::API).tap do |app|
app.include ::API::Helpers::Authentication
app.authenticate_with do |accept|
accept.token_types(:personal_access_token).sent_through(:http_private_token_header)
accept.token_types(:deploy_token).sent_through(:http_deploy_token_header)
accept.token_types(:job_token).sent_through(:http_job_token_header)
end
app.get endpoint do
@project = Project.find(params[:id])
status find_user_from_sources ? 200 : 401
end
end
end
where(:token, :token_header, :token_type) do
ref(:personal_access_token) | 'Private-Token' | 'personal_access_token'
ref(:deploy_token) | 'Deploy-Token' | 'deploy_token'
ref(:ci_job_token) | 'Job-Token' | 'ci_job_token'
end
with_them do
subject(:request) do
get api(endpoint),
params: { id: token_user_project.id },
headers: { REMOTE_ADDR: ip_address, token_header => token }
end
it_behaves_like 'event tracking' do
let(:additional_properties) { { cross_project_request: token_type == 'ci_job_token' ? 'false' : '' } }
let(:property) { token_type }
end
end
end
context 'when the request is authenticated with a job token' do
let(:property) { 'ci_job_token' }
let(:token_param) { :job_token }
context 'when a resource from the same project is requested' do
let(:token) { ci_job_token }
it_behaves_like 'event tracking' do
let(:additional_properties) { { cross_project_request: 'false' } }
end
end
context 'when a resource from another project is requested' do
let(:build) { create(:ci_build, :running, user: token_user) }
let(:token) { build.token }
before do
create(:ci_job_token_project_scope_link,
source_project: token_user_project,
target_project: build.project,
direction: :inbound
)
end
it_behaves_like 'event tracking' do
let(:additional_properties) { { cross_project_request: 'true' } }
end
end
end
end
end
end

View File

@ -44,15 +44,17 @@ RSpec.describe 'Getting Ci Cd Setting', feature_category: :continuous_integratio
it_behaves_like 'a working graphql query'
it 'fetches the settings data' do
expect(settings_data['mergePipelinesEnabled']).to eql project.ci_cd_settings.merge_pipelines_enabled?
expect(settings_data['keepLatestArtifact']).to eql project.keep_latest_artifacts_available?
expect(settings_data['jobTokenScopeEnabled']).to eql project.ci_cd_settings.job_token_scope_enabled?
expect(settings_data['groupRunnersEnabled']).to eql(
project.ci_cd_settings.group_runners_enabled?)
expect(settings_data['inboundJobTokenScopeEnabled']).to eql(
project.ci_cd_settings.inbound_job_token_scope_enabled?)
expect(settings_data['pushRepositoryForJobTokenAllowed']).to eql(
project.ci_cd_settings.push_repository_for_job_token_allowed?)
expect(settings_data['jobTokenScopeEnabled']).to eql project.ci_cd_settings.job_token_scope_enabled?
expect(settings_data['keepLatestArtifact']).to eql project.keep_latest_artifacts_available?
expect(settings_data['mergePipelinesEnabled']).to eql project.ci_cd_settings.merge_pipelines_enabled?
expect(settings_data['pipelineVariablesMinimumOverrideRole']).to eql(
project.ci_pipeline_variables_minimum_override_role)
expect(settings_data['pushRepositoryForJobTokenAllowed']).to eql(
project.ci_cd_settings.push_repository_for_job_token_allowed?)
if Gitlab.ee?
expect(settings_data['mergeTrainsEnabled']).to eql project.ci_cd_settings.merge_trains_enabled?

View File

@ -12,6 +12,49 @@ RSpec.describe 'Query.project(fullPath).pipelines', feature_category: :continuou
travel_to(Time.current) { example.run }
end
describe 'TAGS and BRANCHES scope' do
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:branch_pipeline) { create(:ci_pipeline, project: project, ref: 'feature') }
let_it_be(:tag_pipeline) { create(:ci_pipeline, project: project, ref: 'v1.0.0', tag: true) }
let(:scope) { '' }
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
pipelines(scope: #{scope}) {
nodes {
ref
}
}
}
}
)
end
context 'when the scope is branch pipelines' do
let(:scope) { 'BRANCHES' }
it 'returns all branch pipelines' do
post_graphql(query, current_user: user)
expect(graphql_data_at(:project, :pipelines, :nodes, :ref))
.to contain_exactly(eq(pipeline.ref), eq(branch_pipeline.ref))
end
end
context 'when the scope is tag pipelines' do
let(:scope) { 'TAGS' }
it 'returns all tag pipelines' do
post_graphql(query, current_user: user)
expect(graphql_data_at(:project, :pipelines, :nodes, :ref)).to contain_exactly(eq(tag_pipeline.ref))
end
end
end
describe 'sha' do
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }

View File

@ -11,6 +11,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
let_it_be_with_reload(:project) do
create(:project,
group_runners_enabled: true,
keep_latest_artifact: true,
ci_outbound_job_token_scope_enabled: true,
ci_inbound_job_token_scope_enabled: true
@ -20,6 +21,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
let(:variables) do
{
full_path: project.full_path,
group_runners_enabled: false,
keep_latest_artifact: false,
job_token_scope_enabled: false,
inbound_job_token_scope_enabled: false,
@ -64,6 +66,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
expect(response).to have_gitlab_http_status(:success)
expect(response_errors).to be_blank
expect(project.group_runners_enabled).to be(false)
expect(project.keep_latest_artifact).to be(false)
expect(project.restrict_user_defined_variables?).to be(true)
expect(project.ci_pipeline_variables_minimum_override_role).to eq('no_one_allowed')

View File

@ -443,7 +443,6 @@ RSpec.shared_examples 'work items lock discussion' do
it 'locks and unlocks discussion', :aggregate_failures do
click_button _('More actions'), match: :first
click_button 'Lock discussion'
click_button _('More actions'), match: :first # click again to close the dropdown
expect(page).to have_text "Discussion is locked. Only members can comment."

Some files were not shown because too many files have changed in this diff Show More