Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-07-14 18:12:28 +00:00
parent a14bf87924
commit 9ea23bb2f6
60 changed files with 663 additions and 351 deletions

View File

@ -1413,3 +1413,8 @@ Database/AvoidScopeTo:
RSpec/RedundantMetatagType:
Include:
- '{,ee/,jh/}spec/**/*_spec.rb'
RSpec/Dialect:
# This cop rule produces too many false positives.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/173889#note_2232088196
Enabled: false

View File

@ -3,26 +3,9 @@
Layout/EmptyLinesAroundMethodBody:
Details: grace period
Exclude:
- 'app/components/pajamas/toggle_component.rb'
- 'app/models/concerns/ci/contextable.rb'
- 'app/models/group.rb'
- 'app/models/repository.rb'
- 'app/services/members/destroy_service.rb'
- 'ee/spec/support/helpers/identity_verification_helpers.rb'
- 'lib/api/helpers/packages_helpers.rb'
- 'lib/gitlab/background_migration/batched_migration_job.rb'
- 'lib/gitlab/ci/project_config.rb'
- 'lib/gitlab/cross_project_access.rb'
- 'lib/gitlab/data_builder/push.rb'
- 'lib/gitlab/database/migrations/constraints_helpers.rb'
- 'lib/gitlab/database/partitioning/list/convert_table.rb'
- 'lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb'
- 'lib/gitlab/diff/file.rb'
- 'lib/gitlab/git/repository.rb'
- 'lib/gitlab/gitaly_client/operation_service.rb'
- 'lib/gitlab/import/import_failure_service.rb'
- 'lib/gitlab/memory/upload_and_cleanup_reports.rb'
- 'spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb'
- 'spec/lib/gitlab/popen/runner_spec.rb'
- 'spec/support/helpers/workhorse_lfs_helpers.rb'
- 'tooling/lib/tooling/find_changes.rb'

View File

@ -1,37 +0,0 @@
---
# Cop supports --autocorrect.
RSpec/Dialect:
Exclude:
- 'ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/gon_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/saas_spec.rb'
- 'ee/spec/lib/gitlab/llm/utils/flag_checker_spec.rb'
- 'ee/spec/models/ai/feature_setting_spec.rb'
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/ee/project_spec.rb'
- 'ee/spec/models/gitlab_subscriptions/features_spec.rb'
- 'ee/spec/models/license_spec.rb'
- 'ee/spec/models/merge_request_spec.rb'
- 'ee/spec/models/work_items/type_spec.rb'
- 'ee/spec/policies/project_policy_spec.rb'
- 'ee/spec/requests/api/epic_boards_spec.rb'
- 'ee/spec/requests/api/settings_spec.rb'
- 'ee/spec/services/dashboard/projects/create_service_spec.rb'
- 'ee/spec/services/ee/notes/quick_actions_service_spec.rb'
- 'ee/spec/support/shared_examples/requests/api/graphql/work_item_type_list_ee_shared_examples.rb'
- 'qa/spec/scenario/template_spec.rb'
- 'qa/spec/scenario_shared_examples.rb'
- 'spec/benchmarks/banzai_benchmark.rb'
- 'spec/controllers/projects/pipelines_controller_spec.rb'
- 'spec/factories/projects/ci_feature_usages.rb'
- 'spec/features/issues/list/user_bulk_edits_issues_labels_spec.rb'
- 'spec/features/projects/labels/subscription_spec.rb'
- 'spec/features/projects/labels/update_prioritization_spec.rb'
- 'spec/lib/feature_spec.rb'
- 'spec/lib/gitlab/github_import/label_finder_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/policies/project_policy_spec.rb'
- 'spec/services/projects/update_service_spec.rb'
- 'spec/services/quick_actions/interpret_service_spec.rb'

View File

@ -381,7 +381,6 @@ RSpec/RedundantMetatagType:
- 'spec/models/ci/freeze_period_spec.rb'
- 'spec/models/ci/pipeline_artifact_spec.rb'
- 'spec/models/ci/pipeline_chat_data_spec.rb'
- 'spec/models/ci/pipeline_config_spec.rb'
- 'spec/models/ci/runner_manager_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- 'spec/models/ci/slsa/provenance_statement_spec.rb'

View File

@ -571,14 +571,17 @@ export default {
>
<!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots -->
<li v-if="$slots.status" data-testid="issuable-status" class="!gl-mr-0">
<gl-badge
<button
v-if="!isOpen"
v-gl-tooltip.top
:variant="statusBadgeVariant"
:title="statusTooltip"
:aria-label="statusTooltip"
class="!gl-cursor-default gl-rounded-pill gl-border-none gl-bg-transparent gl-p-0"
>
<slot name="status"></slot>
</gl-badge>
<gl-badge :variant="statusBadgeVariant">
<slot name="status"></slot>
</gl-badge>
</button>
<slot v-else name="status"></slot>
</li>
<slot name="pipeline-status"></slot>

View File

@ -18,8 +18,8 @@ class Pajamas::ToggleComponent < Pajamas::Component
def initialize(
classes:, label: nil, label_position: nil,
id: nil, name: nil, help: nil, data: {},
is_disabled: false, is_checked: false, is_loading: false)
is_disabled: false, is_checked: false, is_loading: false
)
@id = id
@name = name
@classes = classes

View File

@ -155,8 +155,6 @@ module Ci
has_one :source_job, through: :source_pipeline, source: :source_job
has_one :source_bridge, through: :source_pipeline, source: :source_bridge
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
has_one :pipeline_metadata, class_name: 'Ci::PipelineMetadata', inverse_of: :pipeline
has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult',

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
module Ci
class PipelineConfig < Ci::ApplicationRecord
include Ci::Partitionable
self.table_name = :p_ci_pipelines_config
self.primary_key = :pipeline_id
belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :pipeline_config
validates :pipeline, presence: true
validates :content, presence: true
validates :project_id, presence: true
partitionable scope: :pipeline, partitioned: true
end
end

View File

@ -21,8 +21,8 @@ module Ci
expose_project_variables:,
expose_group_variables:,
environment: expanded_environment_name,
dependencies: true)
dependencies: true
)
track_duration do
pipeline
.variables_builder

View File

@ -29,7 +29,6 @@ module Ci
Ci::RunnerManagerBuild
Ci::PipelineArtifact
Ci::PipelineChatData
Ci::PipelineConfig
Ci::PipelineMessage
Ci::PipelineMetadata
Ci::PipelineVariable

View File

@ -962,8 +962,8 @@ class Repository
def revert(
user, commit, branch_name, message,
start_branch_name: nil, start_project: project, dry_run: false)
start_branch_name: nil, start_project: project, dry_run: false
)
with_cache_hooks do
raw_repository.revert(
user: user,
@ -980,8 +980,8 @@ class Repository
def cherry_pick(
user, commit, branch_name, message,
start_branch_name: nil, start_project: project,
author_name: nil, author_email: nil, dry_run: false)
author_name: nil, author_email: nil, dry_run: false
)
target_sha = find_branch(branch_name)&.dereferenced_target&.id if branch_name.present?
with_cache_hooks do

View File

@ -26,7 +26,6 @@ Gitlab::Database::Partitioning.register_models(
Ci::JobArtifact,
Ci::JobArtifactReport,
Ci::Pipeline,
Ci::PipelineConfig,
Ci::PipelineVariable,
Ci::RunnerManagerBuild,
Ci::Stage,

View File

@ -10,4 +10,6 @@ milestone: '12.7'
gitlab_schema: gitlab_ci
sharding_key:
project_id: projects
table_size: small
table_size: small
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/194730
removed_in_milestone: '18.2'

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
class RemoveCiPipelinesConfigPipelineForeignKey < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
FK_NAME = :fk_rails_906c9a2533_p
disable_ddl_transaction!
milestone '18.3'
def up
with_lock_retries do
remove_foreign_key_if_exists :p_ci_pipelines_config, :p_ci_pipelines,
name: FK_NAME, reverse_lock_order: true
end
end
def down
add_concurrent_partitioned_foreign_key(
:p_ci_pipelines_config, :p_ci_pipelines,
name: FK_NAME,
column: [:partition_id, :pipeline_id],
target_column: [:partition_id, :id],
on_update: :cascade,
on_delete: :cascade,
reverse_lock_order: true
)
end
end

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
class DropPCiPipelinesConfig < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers
INDEX_NAME = :index_p_ci_pipelines_config_on_project_id
disable_ddl_transaction!
milestone '18.3'
def up
with_lock_retries do
drop_table :p_ci_pipelines_config
end
end
def down
creation_opts = {
primary_key: [:pipeline_id, :partition_id],
options: 'PARTITION BY LIST (partition_id)',
if_not_exists: true
}
create_table :p_ci_pipelines_config, **creation_opts do |t|
t.bigint :pipeline_id, null: false
t.bigint :partition_id, null: false
t.bigint :project_id
t.text :content, null: false
end
add_not_null_constraint :p_ci_pipelines_config, :project_id
add_concurrent_partitioned_index(:p_ci_pipelines_config, :project_id, name: INDEX_NAME)
end
end

View File

@ -0,0 +1 @@
4343490227963936cd89573cb6cf2b88624df65127ba46c2f7eacfdc1e960ff0

View File

@ -0,0 +1 @@
8c515c4a560891c8e2a1deac3a11f684ecd132b594adce25311b849ae6949dc5

View File

@ -4891,15 +4891,6 @@ CREATE TABLE p_ci_pipelines (
)
PARTITION BY LIST (partition_id);
CREATE TABLE p_ci_pipelines_config (
pipeline_id bigint NOT NULL,
partition_id bigint NOT NULL,
content text NOT NULL,
project_id bigint,
CONSTRAINT check_b2a19dd79a CHECK ((project_id IS NOT NULL))
)
PARTITION BY LIST (partition_id);
CREATE TABLE p_ci_runner_machine_builds (
partition_id bigint NOT NULL,
build_id bigint NOT NULL,
@ -31071,9 +31062,6 @@ ALTER TABLE ONLY p_ci_job_artifacts
ALTER TABLE ONLY p_ci_pipeline_variables
ADD CONSTRAINT p_ci_pipeline_variables_pkey PRIMARY KEY (id, partition_id);
ALTER TABLE ONLY p_ci_pipelines_config
ADD CONSTRAINT p_ci_pipelines_config_pkey PRIMARY KEY (pipeline_id, partition_id);
ALTER TABLE ONLY p_ci_pipelines
ADD CONSTRAINT p_ci_pipelines_pkey PRIMARY KEY (id, partition_id);
@ -37065,8 +37053,6 @@ CREATE INDEX index_p_ci_job_artifact_reports_on_project_id ON ONLY p_ci_job_arti
CREATE INDEX index_p_ci_pipeline_variables_on_project_id ON ONLY p_ci_pipeline_variables USING btree (project_id);
CREATE INDEX index_p_ci_pipelines_config_on_project_id ON ONLY p_ci_pipelines_config USING btree (project_id);
CREATE INDEX index_p_ci_runner_machine_builds_on_project_id ON ONLY p_ci_runner_machine_builds USING btree (project_id);
CREATE INDEX index_p_ci_runner_machine_builds_on_runner_machine_id ON ONLY p_ci_runner_machine_builds USING btree (runner_machine_id);
@ -46049,9 +46035,6 @@ ALTER TABLE ONLY organization_details
ALTER TABLE ONLY members_deletion_schedules
ADD CONSTRAINT fk_rails_8fb4cda076 FOREIGN KEY (scheduled_by_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE p_ci_pipelines_config
ADD CONSTRAINT fk_rails_906c9a2533_p FOREIGN KEY (partition_id, pipeline_id) REFERENCES p_ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY approval_project_rules_groups
ADD CONSTRAINT fk_rails_9071e863d1 FOREIGN KEY (approval_project_rule_id) REFERENCES approval_project_rules(id) ON DELETE CASCADE;

View File

@ -12238,7 +12238,7 @@ Input type: `UpdateNamespacePackageSettingsInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdatenamespacepackagesettingsauditeventsenabled"></a>`auditEventsEnabled` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Deprecated**: **Status**: Experiment. Introduced in GitLab 17.10. |
| <a id="mutationupdatenamespacepackagesettingsauditeventsenabled"></a>`auditEventsEnabled` | [`Boolean`](#boolean) | Indicates whether audit events are created when publishing or deleting a package in the namespace (Premium and Ultimate only). |
| <a id="mutationupdatenamespacepackagesettingsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatenamespacepackagesettingsgenericduplicateexceptionregex"></a>`genericDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
| <a id="mutationupdatenamespacepackagesettingsgenericduplicatesallowed"></a>`genericDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate generic packages are allowed for the namespace. |
@ -35879,7 +35879,7 @@ Namespace-level Package Registry settings.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagesettingsauditeventsenabled"></a>`auditEventsEnabled` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.10. **Status**: Experiment. Indicates whether audit events are created when publishing or deleting a package in the namespace (Premium and Ultimate only). Returns `null` if `package_registry_audit_events` feature flag is disabled. |
| <a id="packagesettingsauditeventsenabled"></a>`auditEventsEnabled` | [`Boolean!`](#boolean) | Indicates whether audit events are created when publishing or deleting a package in the namespace (Premium and Ultimate only). |
| <a id="packagesettingsgenericduplicateexceptionregex"></a>`genericDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
| <a id="packagesettingsgenericduplicatesallowed"></a>`genericDuplicatesAllowed` | [`Boolean!`](#boolean) | Indicates whether duplicate generic packages are allowed for the namespace. |
| <a id="packagesettingslockmavenpackagerequestsforwarding"></a>`lockMavenPackageRequestsForwarding` | [`Boolean!`](#boolean) | Indicates whether Maven package forwarding is locked for all descendent namespaces. |

View File

@ -45221,6 +45221,9 @@ definitions:
type: string
format: date-time
example: '2013-09-30T13:46:02Z'
visibility:
type: string
example: public
namespace:
"$ref": "#/definitions/API_Entities_NamespaceBasic"
custom_attributes:
@ -45270,9 +45273,6 @@ definitions:
type: boolean
archived:
type: boolean
visibility:
type: string
example: public
owner:
"$ref": "#/definitions/API_Entities_UserBasic"
resolve_outdated_diff_discussions:
@ -45671,6 +45671,9 @@ definitions:
type: string
format: date-time
example: '2013-09-30T13:46:02Z'
visibility:
type: string
example: public
namespace:
"$ref": "#/definitions/API_Entities_NamespaceBasic"
custom_attributes:
@ -60921,6 +60924,9 @@ definitions:
type: string
format: date-time
example: '2013-09-30T13:46:02Z'
visibility:
type: string
example: public
namespace:
"$ref": "#/definitions/API_Entities_NamespaceBasic"
custom_attributes:
@ -60970,9 +60976,6 @@ definitions:
type: boolean
archived:
type: boolean
visibility:
type: string
example: public
owner:
"$ref": "#/definitions/API_Entities_UserBasic"
resolve_outdated_diff_discussions:

View File

@ -421,7 +421,7 @@ When `simple=true` or the user is unauthenticated this returns something like:
Example request:
```shell
curl --request GET "https://gitlab.example.com/api/v4/projects"
curl --request GET "https://gitlab.example.com/api/v4/projects?simple=true"
```
Example response:
@ -451,6 +451,7 @@ Example response:
"avatar_url": "https://gitlab.example.com/uploads/project/avatar/4/uploads/avatar.png",
"star_count": 0,
"last_activity_at": "2013-09-30T13:46:02Z",
"visibility": "public",
"namespace": {
"id": 2,
"name": "Diaspora",

View File

@ -198,71 +198,9 @@ http://[your-instance-ip]:8080
## Connect GitLab to GitLab Observability
### Configure GitLab
### Configure GitLab and Enable the feature flag
Add the GitLab O11y URL as an environment variable to your GitLab instance:
{{< tabs >}}
{{< tab title="Linux package (Omnibus)" >}}
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['env'] = {
'O11Y_URL' => 'http://[your-o11y-instance-ip]:8080'
}
```
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
{{< /tab >}}
{{< tab title="Docker" >}}
```shell
docker run --detach \
--hostname gitlab.example.com \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
gitlab/gitlab-ce:latest
```
The `O11Y_URL` environment variable must be configured in the GitLab configuration file:
1. Access the container:
```shell
docker exec -it gitlab /bin/bash
```
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['env'] = {
'O11Y_URL' => 'http://[your-o11y-instance-ip]:8080'
}
```
1. Reconfigure GitLab:
```shell
gitlab-ctl reconfigure
gitlab-ctl restart
```
{{< /tab >}}
{{< /tabs >}}
### Enable the feature flag
The Observability feature is behind a feature flag. To enable it:
Configure the GitLab O11y URL for your group and enable the feature flag using the Rails console:
1. Access the Rails console:
@ -286,18 +224,31 @@ The Observability feature is behind a feature flag. To enable it:
{{< /tabs >}}
1. Enable the feature flag for your group:
1. Configure the observability settings for your group and enable the feature flag:
```ruby
Feature.enable(:observability_sass_features, Group.find_by_path('your-group-name'))
group = Group.find_by_path('your-group-name')
Observability::GroupO11ySetting.create!(
group_id: group.id,
o11y_service_url: 'your-o11y-instance-url',
o11y_service_user_email: 'your-email@example.com',
o11y_service_password: 'your-secure-password',
o11y_service_post_message_encryption_key: 'your-super-secret-encryption-key-here-32-chars-minimum'
)
Feature.enable(:observability_sass_features, group)
Feature.enabled?(:observability_sass_features, group)
```
1. Verify that the feature flag is enabled:
Replace:
- `your-group-name` with your actual group path
- `your-o11y-instance-url` with your GitLab O11y instance URL (for example: `http://192.168.1.100:8080`)
- Email and password with your preferred credentials
- Encryption key with a secure 32+ character string
```ruby
Feature.enabled?(:observability_sass_features, Group.find_by_path('your-group-name'))
# Should return true
```
The last command should return `true` to confirm the feature is enabled.
## Use Observability with GitLab
@ -425,7 +376,8 @@ docker logs [container_name]
1. Check that the O11Y_URL environment variable is set:
```ruby
ENV['O11Y_URL']
group = Group.find_by_path('your-group-name')
group.observability_group_o11y_setting&.o11y_service_url
```
1. Ensure the routes are properly registered:

View File

@ -31,13 +31,6 @@ The subscription determines which features are available for your private projec
Qualifying open source projects also get 50,000 compute minutes and free access to the **Ultimate** tier
through the [GitLab for Open Source program](https://about.gitlab.com/solutions/open-source/).
## Add or change subscription contacts
Contacts can renew a subscription, cancel a subscription, or transfer the subscription to a different namespace.
You can [change profile owner information](../customers_portal.md#change-profile-owner-information)
and [add another billing account manager](../customers_portal.md#add-a-billing-account-manager).
## How seat usage is determined
A GitLab.com subscription uses a concurrent (_seat_) model.

View File

@ -472,6 +472,13 @@ Only one namespace can be linked to a subscription.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a demo, see [Linking GitLab Subscription to the Namespace](https://youtu.be/8iOsN8ajBUw).
## Add or change subscription contacts
Contacts can renew a subscription, cancel a subscription, or transfer the subscription to a different namespace.
You can [change profile owner information](customers_portal.md#change-profile-owner-information)
and [add another billing account manager](customers_portal.md#add-a-billing-account-manager).
### Transfer restrictions
You can change the linked namespace, however this is not supported for all subscription types.

View File

@ -433,6 +433,7 @@ On GitLab Self-Managed, by default the `fallback_behavior` field is available. T
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/498624) support for use in pipeline execution policies in GitLab 17.10 [with a flag](../../../administration/feature_flags/_index.md) named `unblock_rules_using_pipeline_execution_policies`. Enabled by default.
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/525270) in GitLab 18.3. Feature flag `unblock_rules_using_pipeline_execution_policies` removed.
{{< /history >}}

View File

@ -440,8 +440,8 @@ Audit event types belong to the following product categories.
| Type name | Event triggered when | Saved to database | Introduced in | Scope |
|:----------|:---------------------|:------------------|:--------------|:------|
| [`package_registry_package_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178181) | A package was deleted from GitLab package registry. Available only when the feature flag `package_registry_audit_events` is enabled and the namespace's package setting `audit_events_enabled` is true. | {{< icon name="check-circle" >}} Yes | GitLab [17.10](https://gitlab.com/gitlab-org/gitlab/-/issues/329588) | Project, Group |
| [`package_registry_package_published`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178181) | A package was published to GitLab package registry. Available only when the feature flag `package_registry_audit_events` is enabled and the namespace's package setting `audit_events_enabled` is true. | {{< icon name="check-circle" >}} Yes | GitLab [17.9](https://gitlab.com/gitlab-org/gitlab/-/issues/329588) | Project, Group |
| [`package_registry_package_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178181) | A package was deleted from GitLab package registry. Available only when the namespace's package setting `audit_events_enabled` is true. | {{< icon name="check-circle" >}} Yes | GitLab [17.10](https://gitlab.com/gitlab-org/gitlab/-/issues/329588) | Project, Group |
| [`package_registry_package_published`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178181) | A package was published to GitLab package registry. Available only when the namespace's package setting `audit_events_enabled` is true. | {{< icon name="check-circle" >}} Yes | GitLab [17.9](https://gitlab.com/gitlab-org/gitlab/-/issues/329588) | Project, Group |
| [`project_feature_package_registry_access_level_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106919) | A project's package registry access level setting is updated | {{< icon name="check-circle" >}} Yes | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/369296) | Project |
| [`project_packages_enabled_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7962) | The setting that controls packages for a project is updated | {{< icon name="check-circle" >}} Yes | GitLab [11.5](https://gitlab.com/gitlab-org/gitlab/-/issues/369288) | Project |

View File

@ -0,0 +1,129 @@
---
stage: Verify
group: Runner
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
title: Get started with GitLab Runner
---
GitLab Runner administration encompasses the complete lifecycle of managing your CI/CD job execution infrastructure:
- Deploying and registering runners
- Configuring executors for specific workloads
- Scaling capacity to match organizational growth
The process of administering runners is part of a larger workflow:
![Workflow](img/get_started_runner_v18_3.png)
You manage runner access through scopes and tags, monitor performance, and maintain the runner fleet.
## Step 1: Install runners
Install GitLab Runner to create the application that executes CI/CD jobs.
Installation involves downloading and setting up GitLab Runner on your target infrastructure.
The installation process varies depending on the target operating system. GitLab provides binaries
and installation instructions for Linux, Windows, macOS, and z/OS. Choose your installation method
based on your platform and requirements.
For more information, see [install GitLab Runner](https://docs.gitlab.com/runner/install/).
## Step 2: Register runners
Register your runners to establish authenticated communication between your
GitLab instance and the machine where GitLab Runner is installed.
Registration connects individual runners to your GitLab instance using authentication tokens.
During registration, you specify the runner's scope, executor type, and other configuration
parameters that determine how the runner operates.
Before you register a runner, you should determine if you want to limit it to a specific GitLab group or project.
You can configure self-managed runners with different access scopes during registration to determine which projects they're available for:
- Instance runners: Available to all projects on your GitLab instance
- Group runners: Available to all projects in a specific group and its subgroups
- Project runners: Available only to a specific project
When you register a runner, add tags to it
to route jobs to appropriate runners. Assign meaningful tags and reference them
in your `.gitlab-ci.yml` files to ensure jobs run on runners with the required capabilities.
When a CI/CD job runs, it knows which runner to use by looking at the assigned tags.
Tags are the only way to filter the list of available runners for a job.
For more information, see:
- [Register a runner](https://docs.gitlab.com/runner/register/)
- [Migrate to the new runner registration workflow](../../ci/runners/new_creation_workflow.md)
- [Instance runners](../../ci/runners/runners_scope.md#instance-runners)
- [Group runners](../../ci/runners/runners_scope.md#group-runners)
- [Project runners](../../ci/runners/runners_scope.md#project-runners)
- [Tags](../../ci/yaml/_index.md#tags)
## Step 3: Choose executors
GitLab Runner executors are the different environments and methods that GitLab Runner can use to execute CI/CD jobs.
They determine how and where your pipeline jobs actually run. Proper configuration ensures jobs run in appropriate
environments with correct security boundaries.
When you register a runner, you must choose an executor.
GitLab Runner uses an executor system to determine where and how jobs run.
An executor determines the environment each job runs in.
Select executors that match your infrastructure and job requirements.
For example:
- If you want your CI/CD job to run PowerShell commands, you might install GitLab
Runner on a Windows server and then register a runner that uses the shell executor.
- If you want your CI/CD job to run commands in a custom Docker container,
you might install GitLab Runner on a Linux server and register a runner that uses
the Docker executor.
These examples are only a couple of possible configurations. You can install GitLab Runner
on a virtual machine and have it use another virtual machine as an executor.
For more information, see [executors](https://docs.gitlab.com/runner/executors/).
## Step 4: Configure runners and start running jobs
You can configure GitLab Runners by editing the `config.toml` file,
which is automatically generated when you install and register a runner.
In this file you can edit settings for a specific runner, or for all runners.
Configure it to set concurrency limits, logging levels, cache settings, CPU limits, and executor-specific parameters.
Use consistent configurations across your runner fleet.
After a runner is configured and available for your project, your CI/CD jobs can use the runner.
Runners usually process jobs on the same machine where you installed GitLab Runner.
However, you can also have a runner process jobs in a container,
in a Kubernetes cluster, or in auto-scaled instances in the cloud.
For more information, see:
- [Configure GitLab Runners](https://docs.gitlab.com/runner/configuration/advanced-configuration/)
- [CI/CD jobs](../../ci/jobs/_index.md)
## Step 5: Continue to configure, scale, and optimize your runners
Advanced runner features improve job execution efficiency and provide specialized capabilities for complex CI/CD workflows.
These optimizations reduce job runtime and enhance the developer experience through autoscaling, performance monitoring,
fleet management, and specialized configurations.
Autoscaling adjusts runner capacity automatically based on job demand, while performance optimization ensures efficient resource utilization.
These capabilities help you handle variable workloads while controlling infrastructure costs.
Fleet management provides centralized control and monitoring for multiple runners, enabling enterprise-scale runner deployments. Fleet scaling involves coordinating capacity across multiple runners and implementing operational best practices.
Use built-in Prometheus metrics to help you monitor runner health and performance.
You can track key metrics like active job count, CPU utilization, memory usage, job success rates,
and queue lengths to ensure your runners operate efficiently.
For more information, see:
- [Autoscale configuration](https://docs.gitlab.com/runner/runner_autoscale/)
- [Fleet scaling](https://docs.gitlab.com/runner/fleet_scaling/)
- [Runner fleet configuration and best practices](../../topics/runner_fleet_design_guides/_index.md)
- [Monitor runner performance](https://docs.gitlab.com/runner/monitoring/)
- [Runner fleet dashboard](../../ci/runners/runner_fleet_dashboard.md)
- [Long polling](../../ci/runners/long_polling.md)
- [Docker-in-Docker configuration](https://docs.gitlab.com/runner/executors/docker/)
- [GitLab Runner Infrastructure Toolkit (GRIT)](https://gitlab.com/gitlab-org/ci-cd/runner-tools/grit)

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@ -185,6 +185,7 @@ Several known issues exist when you allow anyone to pull from the package regist
{{< history >}}
- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/329588) in GitLab 17.10 [with a flag](../../../administration/feature_flags/_index.md) named `package_registry_audit_events`. Disabled by default.
- [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/554817) in GitLab 18.2. Feature flag `package_registry_audit_events` removed.
{{< /history >}}

View File

@ -38,6 +38,7 @@ module API
expose :star_count, documentation: { type: 'integer', example: 1 }
expose :last_activity_at, documentation: { type: 'dateTime', example: '2013-09-30T13:46:02Z' }
expose :visibility, documentation: { type: 'string', example: 'public' }
expose :namespace, using: 'API::Entities::NamespaceBasic'
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes

View File

@ -47,7 +47,6 @@ module API
expose :packages_enabled, documentation: { type: 'boolean' }
expose :empty_repo?, as: :empty_repo, documentation: { type: 'boolean' }
expose :archived?, as: :archived, documentation: { type: 'boolean' }
expose :visibility, documentation: { type: 'string', example: 'public' }
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :resolve_outdated_diff_discussions, documentation: { type: 'boolean' }
expose :container_expiration_policy,

View File

@ -51,8 +51,8 @@ module API
subject: user_project,
has_length: true,
maximum_size: MAX_PACKAGE_FILE_SIZE,
use_final_store_path: false)
use_final_store_path: false
)
authorize_upload!(subject)
status 200

View File

@ -80,7 +80,6 @@ module Gitlab
batch_table:, batch_column:, sub_batch_size:, pause_ms:, connection:, job_arguments: [],
start_id: nil, end_id: nil, start_cursor: nil, end_cursor: nil, sub_batch_exception: nil
)
@start_id = start_id
@end_id = end_id
@start_cursor = start_cursor

View File

@ -22,8 +22,8 @@ module Gitlab
actions: {},
positive_condition: nil,
negative_condition: nil,
skip: false)
skip: false
)
new_check = CheckInfo.new(actions,
positive_condition,
negative_condition,

View File

@ -98,8 +98,8 @@ module Gitlab
def build(
project:, user:, ref:, oldrev: nil, newrev: nil,
commits: [], commits_count: nil, message: nil, push_options: {},
with_changed_files: true)
with_changed_files: true
)
commits = Array(commits)
# Total commits count

View File

@ -253,8 +253,8 @@ module Gitlab
end
def add_multi_column_not_null_constraint(
table, *columns, limit: 1, operator: '=', constraint_name: nil, validate: true)
table, *columns, limit: 1, operator: '=', constraint_name: nil, validate: true
)
raise 'Expected multiple columns, use add_not_null_constraint for a single column' unless columns.size > 1
add_check_constraint(

View File

@ -15,8 +15,8 @@ module Gitlab
def initialize(
migration_context:, table_name:, parent_table_name:, partitioning_column:,
zero_partition_value:)
zero_partition_value:
)
@migration_context = migration_context
@connection = migration_context.connection
@table_name = table_name

View File

@ -32,8 +32,8 @@ module Gitlab
fallback_diff_refs: nil,
stats: nil,
unique_identifier: nil,
max_blob_size: nil)
max_blob_size: nil
)
@diff = diff
@stats = stats
@repository = repository

View File

@ -1019,8 +1019,8 @@ module Gitlab
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_sha: nil, start_repository: nil,
force: false, sign: true, target_sha: nil)
force: false, sign: true, target_sha: nil
)
wrapped_gitaly_errors do
gitaly_operation_client.user_commit_files(user, branch_name,
message, actions, author_email, author_name, start_branch_name,

View File

@ -268,8 +268,8 @@ module Gitlab
# rubocop:disable Metrics/ParameterLists
def user_cherry_pick(
user:, commit:, branch_name:, message:,
start_branch_name:, start_repository:, author_name: nil, author_email: nil, dry_run: false, target_sha: nil)
start_branch_name:, start_repository:, author_name: nil, author_email: nil, dry_run: false, target_sha: nil
)
request = Gitaly::UserCherryPickRequest.new(
repository: @gitaly_repo,
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
@ -629,8 +629,8 @@ module Gitlab
# rubocop:disable Metrics/ParameterLists
def user_commit_files_request_header(
user, branch_name, commit_message, actions, author_email, author_name,
start_branch_name, start_repository, force, start_sha, sign, target_sha)
start_branch_name, start_repository, force, start_sha, sign, target_sha
)
Gitaly::UserCommitFilesRequestHeader.new(
repository: @gitaly_repo,
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,

View File

@ -9,8 +9,8 @@ module Gitlab
uploader:,
reports_path:,
logger:,
sleep_time_seconds: ENV['GITLAB_DIAGNOSTIC_REPORTS_UPLOADER_SLEEP_S']&.to_i || DEFAULT_SLEEP_TIME_SECONDS)
sleep_time_seconds: ENV['GITLAB_DIAGNOSTIC_REPORTS_UPLOADER_SLEEP_S']&.to_i || DEFAULT_SLEEP_TIME_SECONDS
)
@uploader = uploader
@reports_path = reports_path
@sleep_time_seconds = sleep_time_seconds

View File

@ -278,7 +278,7 @@
"crypto": "^1.0.1",
"custom-jquery-matchers": "^2.1.0",
"dependency-cruiser": "^16.9.0",
"eslint": "9.30.1",
"eslint": "9.31.0",
"eslint-formatter-gitlab": "^6.0.1",
"eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.10",

View File

@ -1,8 +0,0 @@
# frozen_string_literal: true
FactoryBot.define do
factory :ci_pipeline_config, class: 'Ci::PipelineConfig' do
pipeline factory: :ci_empty_pipeline
content { "content" }
end
end

View File

@ -75,7 +75,7 @@ RSpec.describe 'Sandboxed Mermaid rendering', :js, feature_category: :markdown d
project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
end
it 'includes mermaid frame correctly' do
it 'includes mermaid frame correctly', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/554077' do
visit(project_path(project))
wait_for_all_requests

View File

@ -1,14 +1,19 @@
{
"type": "object",
"required" : [
"required": [
"id",
"project",
"lists"
],
"properties" : {
"id": { "type": "integer" },
"properties": {
"id": {
"type": "integer"
},
"project": {
"type": ["object", "null"],
"type": [
"object",
"null"
],
"required": [
"id",
"avatar_url",
@ -29,25 +34,80 @@
"last_activity_at"
],
"properties": {
"id": { "type": "integer" },
"avatar_url": { "type": ["string", "null"] },
"readme_url": { "type": ["string", "null"] },
"description": { "type": ["string", "null"] },
"default_branch": { "type": ["string", "null"] },
"tag_list": { "type": "array" },
"topics": { "type": "array" },
"ssh_url_to_repo": { "type": "string" },
"http_url_to_repo": { "type": "string" },
"web_url": { "type": "string" },
"name": { "type": "string" },
"name_with_namespace": { "type": "string" },
"path": { "type": "string" },
"path_with_namespace": { "type": "string" },
"star_count": { "type": "integer" },
"forks_count": { "type": "integer" },
"created_at": { "type": "string", "format": "date-time" },
"namespace": {"type": "object" },
"last_activity_at": { "type": "string", "format": "date-time" }
"id": {
"type": "integer"
},
"avatar_url": {
"type": [
"string",
"null"
]
},
"readme_url": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"default_branch": {
"type": [
"string",
"null"
]
},
"tag_list": {
"type": "array"
},
"topics": {
"type": "array"
},
"ssh_url_to_repo": {
"type": "string"
},
"http_url_to_repo": {
"type": "string"
},
"web_url": {
"type": "string"
},
"name": {
"type": "string"
},
"name_with_namespace": {
"type": "string"
},
"path": {
"type": "string"
},
"path_with_namespace": {
"type": "string"
},
"star_count": {
"type": "integer"
},
"forks_count": {
"type": "integer"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"namespace": {
"type": "object"
},
"last_activity_at": {
"type": "string",
"format": "date-time"
},
"visibility": {
"type": "string"
}
},
"additionalProperties": false
},
@ -55,15 +115,20 @@
"type": "array",
"items": {
"type": "object",
"required" : [
"required": [
"id",
"label",
"position"
],
"properties" : {
"id": { "type": "integer" },
"properties": {
"id": {
"type": "integer"
},
"label": {
"type": ["object", "null"],
"type": [
"object",
"null"
],
"required": [
"id",
"color",
@ -71,19 +136,42 @@
"name"
],
"properties": {
"id": { "type": "integer" },
"id": {
"type": "integer"
},
"color": {
"type": "string",
"pattern": "#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"
},
"description": { "type": ["string", "null"] },
"name": { "type": "string" }
"description": {
"type": [
"string",
"null"
]
},
"name": {
"type": "string"
}
}
},
"position": { "type": ["integer", "null"] },
"max_issue_count": { "type": "integer" },
"max_issue_weight": { "type": "integer" },
"limit_metric": { "type": ["string", "null"] }
"position": {
"type": [
"integer",
"null"
]
},
"max_issue_count": {
"type": "integer"
},
"max_issue_weight": {
"type": "integer"
},
"limit_metric": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
}

View File

@ -1,14 +1,37 @@
{
"type": "object",
"properties" : {
"id": { "type": "integer" },
"name": { "type": "string" },
"name_with_namespace": { "type": "string" },
"description": { "type": ["string", "null"] },
"path": { "type": "string" },
"path_with_namespace": { "type": "string" },
"created_at": { "type": "string", "format": "date-time" },
"default_branch": { "type": ["string", "null"] },
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"name_with_namespace": {
"type": "string"
},
"description": {
"type": [
"string",
"null"
]
},
"path": {
"type": "string"
},
"path_with_namespace": {
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"default_branch": {
"type": [
"string",
"null"
]
},
"tag_list": {
"type": "array",
"items": {
@ -21,31 +44,87 @@
"type": "string"
}
},
"ssh_url_to_repo": { "type": "string" },
"http_url_to_repo": { "type": "string" },
"web_url": { "type": "string" },
"readme_url": { "type": ["string", "null"] },
"avatar_url": { "type": ["string", "null"] },
"star_count": { "type": "integer" },
"forks_count": { "type": "integer" },
"last_activity_at": { "type": "string", "format": "date-time" },
"ssh_url_to_repo": {
"type": "string"
},
"http_url_to_repo": {
"type": "string"
},
"web_url": {
"type": "string"
},
"readme_url": {
"type": [
"string",
"null"
]
},
"avatar_url": {
"type": [
"string",
"null"
]
},
"star_count": {
"type": "integer"
},
"forks_count": {
"type": "integer"
},
"last_activity_at": {
"type": "string",
"format": "date-time"
},
"visibility": {
"type": "string"
},
"namespace": {
"type": "object",
"properties" : {
"id": { "type": "integer" },
"name": { "type": "string" },
"path": { "type": "string" },
"kind": { "type": "string" },
"full_path": { "type": "string" },
"parent_id": { "type": ["integer", "null"] }
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"path": {
"type": "string"
},
"kind": {
"type": "string"
},
"full_path": {
"type": "string"
},
"parent_id": {
"type": [
"integer",
"null"
]
}
}
}
},
"required": [
"id", "name", "name_with_namespace", "description", "path",
"path_with_namespace", "created_at", "default_branch", "tag_list", "topics",
"ssh_url_to_repo", "http_url_to_repo", "web_url", "readme_url", "avatar_url",
"star_count", "forks_count", "last_activity_at", "namespace"
"id",
"name",
"name_with_namespace",
"description",
"path",
"path_with_namespace",
"created_at",
"default_branch",
"tag_list",
"topics",
"ssh_url_to_repo",
"http_url_to_repo",
"web_url",
"readme_url",
"avatar_url",
"star_count",
"forks_count",
"last_activity_at",
"namespace"
],
"additionalProperties": false
}

View File

@ -580,9 +580,10 @@ describe('IssuableItem', () => {
});
const statusEl = findStatusEl();
const statusBadge = statusEl.findComponent(GlBadge);
const statusBadgeWrapper = statusEl.find('button');
expect(statusBadge.exists()).toBe(true);
expect(statusBadge.attributes('title')).toBe('January 1, 2000 at 12:00:00 AM GMT');
expect(statusBadgeWrapper.attributes('title')).toBe('January 1, 2000 at 12:00:00 AM GMT');
});
it('does not render a tooltip if the issuable doesn\t have a mergedAt value', () => {
@ -599,9 +600,10 @@ describe('IssuableItem', () => {
});
const statusEl = findStatusEl();
const statusBadge = statusEl.findComponent(GlBadge);
const statusBadgeWrapper = statusEl.find('button');
expect(statusBadge.exists()).toBe(true);
expect(statusBadge.attributes('title')).toBe('');
expect(statusBadgeWrapper.attributes('title')).toBe('');
});
it('renders issuable status without badge if open', () => {

View File

@ -89,4 +89,37 @@ RSpec.describe API::Entities::BasicProjectDetails, feature_category: :api do
end
end
end
describe '#visibility' do
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:internal_project) { create(:project, :internal) }
context 'with public project' do
let(:project) { public_project }
let(:current_user) { nil }
it 'exposes visibility as public' do
expect(output).to include visibility: 'public'
end
end
context 'with private project' do
let(:project) { private_project }
let(:current_user) { project.first_owner }
it 'exposes visibility as private' do
expect(output).to include visibility: 'private'
end
end
context 'with internal project' do
let(:project) { internal_project }
let(:current_user) { project.first_owner }
it 'exposes visibility as internal' do
expect(output).to include visibility: 'internal'
end
end
end
end

View File

@ -15,7 +15,6 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
batch_size: Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers::BATCH_SIZE,
sub_batch_size: Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers::SUB_BATCH_SIZE
)
batch_max_value = define_batchable_model(batch_table_name, connection: connection).maximum(batch_column_name)
Gitlab::Database::SharedModel.using_connection(connection) do

View File

@ -91,7 +91,6 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :organizatio
'p_ci_job_annotations.project_id', # LFK already present on p_ci_builds and cascade delete all ci resources
'ci_build_pending_states.project_id', # LFK already present on p_ci_builds and cascade delete all ci resources
'ci_builds_runner_session.project_id', # LFK already present on p_ci_builds and cascade delete all ci resources
'p_ci_pipelines_config.project_id', # LFK already present on p_ci_pipelines and cascade delete all ci resources
'ci_resources.project_id', # LFK already present on ci_resource_groups and cascade delete all ci resources
'ci_unit_test_failures.project_id', # LFK already present on ci_unit_tests and cascade delete all ci resources
'dast_profiles_pipelines.project_id', # LFK already present on dast_profiles and will cascade delete

View File

@ -119,8 +119,8 @@ RSpec.describe Gitlab::Popen::Runner do
stderr: '',
exitstatus: 0,
status: double(exitstatus: exitstatus, success?: exitstatus == 0),
duration: 0.1)
duration: 0.1
)
result =
Gitlab::Popen::Result.new(command, stdout, stderr, status, duration)

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::PipelineConfig, type: :model, feature_category: :continuous_integration do
it { is_expected.to belong_to(:pipeline) }
it { is_expected.to validate_presence_of(:pipeline) }
it { is_expected.to validate_presence_of(:content) }
describe 'partitioning' do
include Ci::PartitioningHelpers
let(:pipeline) { create(:ci_pipeline) }
let(:pipeline_config) { create(:ci_pipeline_config, pipeline: pipeline, project_id: pipeline.project_id) }
before do
stub_current_partition_id(ci_testing_partition_id)
end
it 'assigns the same partition id as the one that pipeline has' do
expect(pipeline_config.partition_id).to eq(ci_testing_partition_id)
end
end
end

View File

@ -75,7 +75,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
it { is_expected.to have_one(:chat_data) }
it { is_expected.to have_one(:triggered_by_pipeline) }
it { is_expected.to have_one(:source_job) }
it { is_expected.to have_one(:pipeline_config) }
it { is_expected.to have_one(:pipeline_metadata) }
it { is_expected.to have_one(:workload) }

View File

@ -210,7 +210,6 @@ RSpec.describe Ci::Partitionable, feature_category: :continuous_integration do
Ci::JobAnnotation
Ci::JobArtifact
Ci::JobArtifactReport
Ci::PipelineConfig
Ci::PipelineVariable
Ci::RunnerManagerBuild
Ci::Stage

View File

@ -1595,7 +1595,6 @@ RSpec.describe API::Groups, :with_current_organization, feature_category: :group
expect(json_response.length).to eq(3)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name, archived_project.name])
expect(json_response.first['visibility']).not_to be_present
end
it "filters the groups projects", :aggregate_failures do
@ -1823,7 +1822,6 @@ RSpec.describe API::Groups, :with_current_organization, feature_category: :group
expect(json_response.length).to eq(2)
project_ids = json_response.map { |project| project['id'] }
expect(project_ids).to match_array([project2.id, project4.id])
expect(json_response.first['visibility']).not_to be_present
end
it 'filters the shared projects in the group based on visibility', :aggregate_failures do

View File

@ -5,8 +5,8 @@ module WorkhorseLfsHelpers
def put_finalize(
lfs_tmp = nil, with_tempfile: false, verified: true, remote_object: nil,
args: {}, to_project: nil, size: nil, sha256: nil)
args: {}, to_project: nil, size: nil, sha256: nil
)
lfs_tmp ||= "#{sample_oid}012345678"
to_project ||= project
uploaded_file =

View File

@ -5790,7 +5790,6 @@
- './spec/models/ci/pending_build_spec.rb'
- './spec/models/ci/persistent_ref_spec.rb'
- './spec/models/ci/pipeline_artifact_spec.rb'
- './spec/models/ci/pipeline_config_spec.rb'
- './spec/models/ci/pipeline_message_spec.rb'
- './spec/models/ci/pipeline_schedule_spec.rb'
- './spec/models/ci/pipeline_schedule_variable_spec.rb'

View File

@ -49,13 +49,22 @@ type countingReadCloser struct {
}
func (crc *countingReadCloser) Read(p []byte) (n int, err error) {
n, err = crc.reader.Read(p)
newCount := atomic.AddInt64(&crc.count, int64(n))
if crc.mode == ModeEnforced && newCount > crc.limit {
return n, &RequestBodyTooLargeError{Limit: crc.limit, Read: newCount}
// Validate the next read to avoid EOF errors
if crc.mode == ModeEnforced {
currentCount := atomic.LoadInt64(&crc.count)
remaining := crc.limit - currentCount
if remaining <= 0 {
return 0, &RequestBodyTooLargeError{Limit: crc.limit, Read: currentCount}
}
// Only read up to the remaining limit
if int64(len(p)) > remaining {
p = p[:remaining]
}
}
n, err = crc.reader.Read(p)
atomic.AddInt64(&crc.count, int64(n))
return n, err
}

View File

@ -6,6 +6,7 @@ import (
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"github.com/stretchr/testify/require"
@ -59,7 +60,16 @@ func TestCountingReadCloser(t *testing.T) {
name: "exceeds limit in enforced mode",
data: "hello world this is too long",
readSizes: []int{28},
expectedCount: 28,
expectedCount: 10,
mode: ModeEnforced,
limit: 10,
expectError: false,
},
{
name: "multiple reads, exceeds limit in enforced mode",
data: "hello world this is too long",
readSizes: []int{15, 15},
expectedCount: 10,
mode: ModeEnforced,
limit: 10,
expectError: true,
@ -520,6 +530,74 @@ func TestIntegration(t *testing.T) {
}
}
func TestStressLimitEnforcement(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write([]byte("success"))
})
server := httptest.NewServer(handler)
defer server.Close()
transport := &contextSettingTransport{
next: http.DefaultTransport,
bodyLimit: 100,
mode: ModeEnforced,
}
// Run many concurrent requests
type testResult struct {
id int
statusCode int
err error
}
const numRequests = 100
results := make(chan testResult, numRequests)
var wg sync.WaitGroup
for i := 0; i < numRequests; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
client := &http.Client{Transport: transport}
largeData := strings.Repeat("a", 150) // Exceed limit
resp, err := client.Post(server.URL, "text/plain", strings.NewReader(largeData))
if err != nil {
results <- testResult{id: id, err: err}
return
}
defer closeResponseBody(resp)
results <- testResult{id: id, statusCode: resp.StatusCode}
}(i)
}
wg.Wait()
close(results)
// Count results
statusCounts := make(map[int]int)
for result := range results {
if result.err != nil {
t.Errorf("id %d: unexpected error: %v", result.id, result.err)
}
statusCounts[result.statusCode]++
}
// ALL requests should return 413 (no 200 responses)
require.Equal(t, numRequests, statusCounts[413], "Expected %d requests with 413 status, got %d. Status counts: %v", numRequests, statusCounts[413], statusCounts)
require.Equal(t, 0, statusCounts[200], "Expected 0 requests with 200 status, got %d. Status counts: %v", statusCounts[200], statusCounts)
}
// contextSettingTransport wraps the request body roundtripper and sets context
type contextSettingTransport struct {
next http.RoundTripper

View File

@ -1286,13 +1286,6 @@
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.0.tgz#3e09a90dfb87e0005c7694791e58e97077271286"
integrity sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==
"@eslint/core@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.14.0.tgz#326289380968eaf7e96f364e1e4cf8f3adf2d003"
integrity sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==
dependencies:
"@types/json-schema" "^7.0.15"
"@eslint/core@^0.15.0":
version "0.15.0"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.0.tgz#8fc04709a7b9a179d9f7d93068fc000cb8c5603d"
@ -1315,10 +1308,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@9.30.1", "@eslint/js@^9.15.0":
version "9.30.1"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.30.1.tgz#ebe9dd52a38345784c486300175a28c6013c088d"
integrity sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==
"@eslint/js@9.31.0", "@eslint/js@^9.15.0":
version "9.31.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.31.0.tgz#adb1f39953d8c475c4384b67b67541b0d7206ed8"
integrity sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==
"@eslint/object-schema@^2.1.6":
version "2.1.6"
@ -7511,18 +7504,18 @@ eslint-visitor-keys@^4.2.1:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
eslint@9.30.1:
version "9.30.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.30.1.tgz#d4107b39964412acd9b5c0744f1c6df514fa1211"
integrity sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==
eslint@9.31.0:
version "9.31.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.31.0.tgz#9a488e6da75bbe05785cd62e43c5ea99356d21ba"
integrity sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.12.1"
"@eslint/config-array" "^0.21.0"
"@eslint/config-helpers" "^0.3.0"
"@eslint/core" "^0.14.0"
"@eslint/core" "^0.15.0"
"@eslint/eslintrc" "^3.3.1"
"@eslint/js" "9.30.1"
"@eslint/js" "9.31.0"
"@eslint/plugin-kit" "^0.3.1"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"