Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-03-17 09:13:02 +00:00
parent 1b8bee4713
commit 64879e0d25
23 changed files with 108 additions and 99 deletions

View File

@ -1 +1 @@
eb76930d3eed5e5faac7155d2d403aa69ff3baa2
f83abd051b37dee24d683ca73db736f392e183e6

View File

@ -14,27 +14,4 @@ class Dashboard::TodosController < Dashboard::ApplicationController
push_frontend_feature_flag(:todos_bulk_actions, current_user)
end
def destroy
todo = current_user.todos.find(params[:id])
TodoService.new.resolve_todo(todo, current_user, resolved_by_action: :mark_done)
respond_to do |format|
format.html do
redirect_to dashboard_todos_path, status: :found, notice: _('To-do item successfully marked as done.')
end
format.js { head :ok }
format.json { render json: todos_counts }
end
end
private
def todos_counts
{
count: current_user.todos_pending_count,
done_count: current_user.todos_done_count
}
end
end

View File

@ -187,7 +187,7 @@ class Namespace < ApplicationRecord
delegate :math_rendering_limits_enabled?,
:lock_math_rendering_limits_enabled?,
to: :namespace_settings
delegate :add_creator, :pending_delete, :pending_delete=, :deleted_at, :deleted_at=,
delegate :add_creator, :deleted_at, :deleted_at=,
to: :namespace_details
delegate :resource_access_token_notify_inherited,
:resource_access_token_notify_inherited=,

View File

@ -264,19 +264,12 @@ class Todo < ApplicationRecord
distinct.pluck(:user_id)
end
# Count todos grouped by user_id and state, using an UNION query
# so we can utilize the partial indexes for each state.
def count_grouped_by_user_id_and_state
grouped_count = select(:user_id, 'count(id) AS count').group(:user_id)
done = grouped_count.where(state: :done).select("'done' AS state")
pending = grouped_count.where(state: :pending).select("'pending' AS state")
union = unscoped.from_union([done, pending], remove_duplicates: false)
.select(:user_id, :count, :state)
connection.select_all(union).each_with_object({}) do |row, counts|
counts[[row['user_id'], row['state']]] = row['count']
end
# Count pending todos grouped by user_id and state
# so we can utilize the index on state / user id.
def pending_count_by_user_id
where(state: :pending)
.group(:user_id)
.count(:id)
end
end

View File

@ -2185,12 +2185,6 @@ class User < ApplicationRecord
end
end
def todos_done_count(force: false)
Rails.cache.fetch(['users', id, 'todos_done_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
TodosFinder.new(self, state: :done).execute.count
end
end
def todos_pending_count(force: false)
Rails.cache.fetch(['users', id, 'todos_pending_count'], force: force, expires_in: COUNT_CACHE_VALIDITY_PERIOD) do
TodosFinder.new(self, state: :pending).execute.count
@ -2204,7 +2198,6 @@ class User < ApplicationRecord
end
def update_todos_count_cache
todos_done_count(force: true)
todos_pending_count(force: true)
end
@ -2226,7 +2219,6 @@ class User < ApplicationRecord
end
def invalidate_todos_cache_counts
Rails.cache.delete(['users', id, 'todos_done_count'])
Rails.cache.delete(['users', id, 'todos_pending_count'])
end

View File

@ -13,25 +13,18 @@ module Users
def execute
user_ids.each_slice(QUERY_BATCH_SIZE) do |user_ids_batch|
todo_counts = Todo.for_user(user_ids_batch).count_grouped_by_user_id_and_state
todo_counts = Todo.for_user(user_ids_batch).pending_count_by_user_id
user_ids_batch.each do |user_id|
update_count_cache(user_id, todo_counts, :done)
update_count_cache(user_id, todo_counts, :pending)
count = todo_counts.fetch(user_id, 0)
Rails.cache.write(
['users', user_id, "todos_pending_count"],
count,
expires_in: User::COUNT_CACHE_VALIDITY_PERIOD
)
end
end
end
private
def update_count_cache(user_id, todo_counts, state)
count = todo_counts.fetch([user_id, state.to_s], 0)
Rails.cache.write(
['users', user_id, "todos_#{state}_count"],
count,
expires_in: User::COUNT_CACHE_VALIDITY_PERIOD
)
end
end
end

View File

@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110010
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/389235
milestone: '15.9'
type: ops
group: group::anti-abuse
group: group::test governance
default_enabled: false

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class AddIndexToProjectRequirementStatusNamesapceUpdatedId < Gitlab::Database::Migration[2.2]
milestone '17.10'
disable_ddl_transaction!
INDEX_NAMESPACE_ID = 'idx_project_requirement_statuses_on_namespace_id'
INDEX_NAMESPACE_UPDATED_AT_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_updated_at_id'
def up
add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :updated_at, :id],
order: { updated_at: :desc, id: :desc }, using: :btree, name: INDEX_NAMESPACE_UPDATED_AT_ID_DESC
remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_ID
end
def down
add_concurrent_index :project_requirement_compliance_statuses, :namespace_id, name: INDEX_NAMESPACE_ID
remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_UPDATED_AT_ID_DESC
end
end

View File

@ -0,0 +1 @@
158ff3fc38ec0c89115fddab9f53b4b332eef85ca82d3d69db2ce6634c50e52d

View File

@ -31579,6 +31579,8 @@ CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_packag
CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name);
CREATE INDEX i_project_requirement_statuses_on_namespace_id_updated_at_id ON project_requirement_compliance_statuses USING btree (namespace_id, updated_at DESC, id DESC);
CREATE INDEX i_protected_branch_unprotect_access_levels_protected_branch_nam ON protected_branch_unprotect_access_levels USING btree (protected_branch_namespace_id);
CREATE INDEX i_protected_branch_unprotect_access_levels_protected_branch_pro ON protected_branch_unprotect_access_levels USING btree (protected_branch_project_id);
@ -31905,8 +31907,6 @@ CREATE INDEX idx_project_repository_check_partial ON projects USING btree (repos
CREATE INDEX idx_project_requirement_statuses_on_framework_id ON project_requirement_compliance_statuses USING btree (compliance_framework_id);
CREATE INDEX idx_project_requirement_statuses_on_namespace_id ON project_requirement_compliance_statuses USING btree (namespace_id);
CREATE INDEX idx_projects_api_created_at_id_for_archived ON projects USING btree (created_at, id) WHERE ((archived = true) AND (pending_delete = false) AND (hidden = false));
CREATE INDEX idx_projects_api_created_at_id_for_archived_vis20 ON projects USING btree (created_at, id) WHERE ((archived = true) AND (visibility_level = 20) AND (pending_delete = false) AND (hidden = false));

View File

@ -17818,6 +17818,29 @@ The edge type for [`PipelineTrigger`](#pipelinetrigger).
| <a id="pipelinetriggeredgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="pipelinetriggeredgenode"></a>`node` | [`PipelineTrigger`](#pipelinetrigger) | The item at the end of the edge. |
#### `ProjectComplianceRequirementStatusConnection`
The connection type for [`ProjectComplianceRequirementStatus`](#projectcompliancerequirementstatus).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectcompliancerequirementstatusconnectionedges"></a>`edges` | [`[ProjectComplianceRequirementStatusEdge]`](#projectcompliancerequirementstatusedge) | A list of edges. |
| <a id="projectcompliancerequirementstatusconnectionnodes"></a>`nodes` | [`[ProjectComplianceRequirementStatus]`](#projectcompliancerequirementstatus) | A list of nodes. |
| <a id="projectcompliancerequirementstatusconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `ProjectComplianceRequirementStatusEdge`
The edge type for [`ProjectComplianceRequirementStatus`](#projectcompliancerequirementstatus).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectcompliancerequirementstatusedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="projectcompliancerequirementstatusedgenode"></a>`node` | [`ProjectComplianceRequirementStatus`](#projectcompliancerequirementstatus) | The item at the end of the edge. |
#### `ProjectConnection`
The connection type for [`Project`](#project).
@ -26805,6 +26828,7 @@ GPG signature for a signed commit.
| <a id="grouppendingmembers"></a>`pendingMembers` {{< icon name="warning-solid" >}} | [`PendingMemberInterfaceConnection`](#pendingmemberinterfaceconnection) | **Introduced** in GitLab 16.6. **Status**: Experiment. A pending membership of a user within this group. |
| <a id="grouppermanentdeletiondate"></a>`permanentDeletionDate` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 16.11. **Status**: Experiment. Date when group will be deleted if delayed group deletion is enabled. |
| <a id="groupproductanalyticsstoredeventslimit"></a>`productAnalyticsStoredEventsLimit` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 16.9. **Status**: Experiment. Number of product analytics events namespace is permitted to store per cycle. |
| <a id="groupprojectcompliancerequirementsstatus"></a>`projectComplianceRequirementsStatus` {{< icon name="warning-solid" >}} | [`ProjectComplianceRequirementStatusConnection`](#projectcompliancerequirementstatusconnection) | **Introduced** in GitLab 17.10. **Status**: Experiment. Compliance standards adherence for the projects in a group and its subgroups. |
| <a id="groupprojectcreationlevel"></a>`projectCreationLevel` | [`String`](#string) | Permission level required to create projects in the group. |
| <a id="groupprojectscount"></a>`projectsCount` | [`Int!`](#int) | Count of direct projects in the group. |
| <a id="grouprecentissueboards"></a>`recentIssueBoards` | [`BoardConnection`](#boardconnection) | List of recently visited boards of the group. Maximum size is 4. (see [Connections](#connections)) |
@ -36254,6 +36278,23 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="projectcicdsettingproject"></a>`project` | [`Project`](#project) | Project the CI/CD settings belong to. |
| <a id="projectcicdsettingpushrepositoryforjobtokenallowed"></a>`pushRepositoryForJobTokenAllowed` | [`Boolean`](#boolean) | Indicates the ability to push to the original project repository using a job token. |
### `ProjectComplianceRequirementStatus`
Compliance requirement status for a project.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectcompliancerequirementstatuscomplianceframework"></a>`complianceFramework` | [`ComplianceFramework!`](#complianceframework) | Framework of the compliance status. |
| <a id="projectcompliancerequirementstatuscompliancerequirement"></a>`complianceRequirement` | [`ComplianceRequirement!`](#compliancerequirement) | Requirement of the compliance status. |
| <a id="projectcompliancerequirementstatusfailcount"></a>`failCount` | [`Int!`](#int) | Total no. of failed compliance controls for the requirement. |
| <a id="projectcompliancerequirementstatusid"></a>`id` | [`ID!`](#id) | Compliance requirement status ID. |
| <a id="projectcompliancerequirementstatuspasscount"></a>`passCount` | [`Int!`](#int) | Total no. of passed compliance controls for the requirement. |
| <a id="projectcompliancerequirementstatuspendingcount"></a>`pendingCount` | [`Int!`](#int) | Total no. of pending compliance controls for the requirement. |
| <a id="projectcompliancerequirementstatusproject"></a>`project` | [`Project!`](#project) | Project of the compliance status. |
| <a id="projectcompliancerequirementstatusupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp when the requirement status was last updated. |
### `ProjectDataTransfer`
#### Fields

View File

@ -234,13 +234,17 @@ it_behaves_like 'internal event tracking' do
end
```
These legacy options are now deprecated:
If present in the context, the following legacy options will be respected by the shared example but are discouraged:
- `label`
- `property`
- `value`
Prefer using `additional_properties` instead.
Prefer including these attributes via `additional_properties` instead.
```ruby
let(:additional_properties) { { label: "value" } }
```
#### Composable matchers

View File

@ -15,13 +15,14 @@ title: Configure GitLab Duo on a self-managed instance
GitLab Duo is powered by large language models (LLMs), with data sent through an AI gateway.
To use GitLab Duo on a self-managed instance, you can do either of the following:
- Use the LLMs and the cloud-based AI gateway that's hosted by GitLab. This is the default option.
- [Use LLMs from the supported list and self-host the AI gateway and LLMs](../../administration/gitlab_duo_self_hosted/_index.md#set-up-a-gitlab-duo-self-hosted-infrastructure).
- Use the GitLab AI vendor models and the cloud-based AI gateway thats hosted by
GitLab. This is the default option.
- [Use GitLab Duo Self-Hosted to self-host the AI gateway, with a supported self-hosted LLM](../../administration/gitlab_duo_self_hosted/_index.md#set-up-a-gitlab-duo-self-hosted-infrastructure).
This option provides full control over your data and security.
{{< alert type="note" >}}
You must have an Ultimate license with GitLab Duo Enterprise add-on to use GitLab Duo Self-Hosted.
You must have an Ultimate subscription with the GitLab Duo Enterprise add-on to use GitLab Duo Self-Hosted.
{{< /alert >}}

View File

@ -19,6 +19,12 @@ GitLab Duo features that are generally available are automatically turned on for
[you must also assign seats](../../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats)
to the users you want to have access.
{{< alert type="note" >}}
To turn on GitLab Duo Self-Hosted, see [Configure GitLab to access GitLab Duo Self-Hosted](../../administration/gitlab_duo_self_hosted/configure_duo_features.md).
{{< /alert >}}
## Turn off GitLab Duo features
You can turn off GitLab Duo for a group, project, or instance.

View File

@ -17,7 +17,7 @@ title: Test a new look for issues
- [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/9584) in GitLab 17.5 [with a flag](../../../administration/feature_flags.md) named `work_items_view_preference`. Disabled by default. This feature is in [beta](../../../policy/development_stages_support.md#beta).
- Feature flag named `work_items_view_preference` enabled on GitLab.com in GitLab 17.9 for a subset of users.
- Feature flag named `work_items_view_preference` [enabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/184496) on GitLab Self-Managed and GitLab Dedicated in 17.10.
- Feature flag named `work_items_view_preference` [enabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/184496) on GitLab.com, GitLab Self-Managed, and GitLab Dedicated in 17.10.
{{< /history >}}

View File

@ -61043,9 +61043,6 @@ msgstr ""
msgid "To-Do List"
msgstr ""
msgid "To-do item successfully marked as done."
msgstr ""
msgid "Today"
msgstr ""

View File

@ -647,7 +647,7 @@ RSpec.describe Todo, feature_category: :notifications do
end
end
describe '.group_by_user_id_and_state' do
describe '.pending_count_by_user_id' do
before do
create(:todo, user: user, state: :pending)
create(:todo, user: user, state: :pending)
@ -656,7 +656,7 @@ RSpec.describe Todo, feature_category: :notifications do
end
specify do
expect(described_class.count_grouped_by_user_id_and_state).to eq({ [user.id, "done"] => 1, [user.id, "pending"] => 2, [user2.id, "pending"] => 1 })
expect(described_class.pending_count_by_user_id).to eq({ user.id => 2, user2.id => 1 })
end
end

View File

@ -3129,11 +3129,9 @@ RSpec.describe User, feature_category: :user_profile do
expect { user.ban }
.to change { todo_users.map(&:todos_pending_count).uniq }.from([1]).to([0])
.and not_change { todo_users.map(&:todos_done_count) }
expect { user.unban }
.to change { todo_users.map(&:todos_pending_count).uniq }.from([0]).to([1])
.and not_change { todo_users.map(&:todos_done_count) }
end
end

View File

@ -78,7 +78,6 @@ RSpec.describe Members::DestroyService, feature_category: :groups_and_projects d
expect(member_user.assigned_open_merge_requests_count).to be(1)
expect(member_user.assigned_open_issues_count).to be(1)
expect(member_user.todos_pending_count).to be(1)
expect(member_user.todos_done_count).to be(1)
service = described_class.new(current_user)
@ -91,7 +90,6 @@ RSpec.describe Members::DestroyService, feature_category: :groups_and_projects d
expect(member_user.assigned_open_merge_requests_count).to be(0)
expect(member_user.assigned_open_issues_count).to be(0)
expect(member_user.todos_pending_count).to be(0)
expect(member_user.todos_done_count).to be(0)
unless opts[:unassign_issuables]
expect(member_user.assigned_merge_requests.opened.count).to be(1)

View File

@ -365,13 +365,11 @@ RSpec.describe TodoService, feature_category: :notifications do
it 'updates when todos change' do
create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author)
expect(john_doe.todos_done_count).to eq(0)
expect(john_doe.todos_pending_count).to eq(1)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.resolve_todos_for_target(issue, john_doe)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(0)
end
end
@ -790,13 +788,11 @@ RSpec.describe TodoService, feature_category: :notifications do
it 'updates when todos change' do
create(:todo, :assigned, user: john_doe, project: project, target: work_item, author: author)
expect(john_doe.todos_done_count).to eq(0)
expect(john_doe.todos_pending_count).to eq(1)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.resolve_todos_for_target(work_item, john_doe)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(0)
end
end
@ -1422,13 +1418,11 @@ RSpec.describe TodoService, feature_category: :notifications do
context 'cached counts' do
it 'updates when todos change' do
expect(john_doe.todos_done_count).to eq(0)
expect(john_doe.todos_pending_count).to eq(1)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.resolve_todo(todo, john_doe)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(0)
end
end
@ -1508,13 +1502,11 @@ RSpec.describe TodoService, feature_category: :notifications do
context 'cached counts' do
it 'updates when todos change' do
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(0)
expect(john_doe).to receive(:update_todos_count_cache).and_call_original
service.restore_todo(todo, john_doe)
expect(john_doe.todos_done_count).to eq(0)
expect(john_doe.todos_pending_count).to eq(1)
end
end

View File

@ -28,7 +28,6 @@ RSpec.describe Todos::Destroy::DestroyedIssuableService, feature_category: :team
it 'invalidates todos cache counts of todo users', :use_clean_rails_redis_caching do
expect { subject }
.to change { pending_todo.user.todos_pending_count }.from(1).to(0)
.and change { done_todo.user.todos_done_count }.from(1).to(0)
end
end

View File

@ -23,23 +23,17 @@ RSpec.describe Users::UpdateTodoCountCacheService, feature_category: :notificati
end
it 'updates the todos_counts for users', :use_clean_rails_memory_store_caching do
Rails.cache.write(['users', user1.id, 'todos_done_count'], 0)
Rails.cache.write(['users', user1.id, 'todos_pending_count'], 0)
Rails.cache.write(['users', user2.id, 'todos_done_count'], 0)
Rails.cache.write(['users', user2.id, 'todos_pending_count'], 0)
expect { execute_all }
.to change(user1, :todos_done_count).from(0).to(2)
.and change(user1, :todos_pending_count).from(0).to(1)
.and change(user2, :todos_done_count).from(0).to(1)
.to change(user1, :todos_pending_count).from(0).to(1)
.and change(user2, :todos_pending_count).from(0).to(2)
Todo.delete_all
expect { execute_all }
.to change(user1, :todos_done_count).from(2).to(0)
.and change(user1, :todos_pending_count).from(1).to(0)
.and change(user2, :todos_done_count).from(1).to(0)
.to change(user1, :todos_pending_count).from(1).to(0)
.and change(user2, :todos_pending_count).from(2).to(0)
end
@ -59,7 +53,7 @@ RSpec.describe Users::UpdateTodoCountCacheService, feature_category: :notificati
it 'sets the correct cache expire time' do
expect(Rails.cache).to receive(:write)
.with(['users', user1.id, anything], anything, expires_in: User::COUNT_CACHE_VALIDITY_PERIOD)
.twice
.once
execute_single
end

View File

@ -11,11 +11,12 @@
# - additional_properties
# - event_attribute_overrides - is used when its necessary to override the attributes available in parent context.
#
# These legacy options are now deprecated:
# [Legacy] If present in the context, the following will be respected by the shared example but are discouraged:
# - label
# - property
# - value
# Prefer using additional_properties instead.
# [Recommended] Prefer including these attributes via additional_properties instead.
# ex) let(:additional_properties) { { label: "value" } }
RSpec.shared_examples 'internal event tracking' do
let(:all_metrics) do