summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-10 09:08:55 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-10 09:08:55 +0000
commita2344dbf1942dc3919c55b0684d2566368e03852 (patch)
tree00f92dc8b5e2e2cb732662f5e5e0ffd5f3d2eca0
parent213da19cda5309148952ab770e2a9e122fe32e22 (diff)
downloadgitlab-ce-a2344dbf1942dc3919c55b0684d2566368e03852.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/CODEOWNERS19
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue2
-rw-r--r--app/assets/stylesheets/framework/super_sidebar.scss4
-rw-r--r--app/controllers/projects/issues_controller.rb6
-rw-r--r--app/finders/deployments_finder.rb10
-rw-r--r--app/models/work_item.rb4
-rw-r--r--app/services/ci/pipelines/add_job_service.rb6
-rw-r--r--app/services/git/base_hooks_service.rb8
-rw-r--r--app/services/quick_actions/interpret_service.rb1
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/clusters/agents/notify_git_push_worker.rb23
-rw-r--r--config/feature_flags/development/ci_include_components.yml2
-rw-r--r--config/feature_flags/development/deployments_raise_updated_at_inefficient_error.yml4
-rw-r--r--config/feature_flags/development/deployments_raise_updated_at_inefficient_error_override.yml8
-rw-r--r--config/feature_flags/development/notify_kas_on_git_push.yml8
-rw-r--r--config/metrics/counts_28d/20210216181147_service_desk_enabled_projects.yml6
-rw-r--r--config/metrics/counts_28d/20210216181148_service_desk_issues.yml6
-rw-r--r--config/metrics/counts_28d/20230420160442_i_quickactions_type_monthly.yml25
-rw-r--r--config/metrics/counts_7d/20230420160441_i_quickactions_type_weekly.yml25
-rw-r--r--config/metrics/counts_all/20210216175024_service_desk_enabled_projects.yml6
-rw-r--r--config/metrics/counts_all/20210216175026_service_desk_issues.yml6
-rw-r--r--config/metrics/counts_all/20210216181122_service_desk_enabled_projects.yml6
-rw-r--r--config/metrics/counts_all/20210216181124_service_desk_issues.yml6
-rw-r--r--doc/development/snowplow/review_guidelines.md4
-rw-r--r--doc/user/profile/index.md2
-rw-r--r--doc/user/project/quick_actions.md1
-rw-r--r--lib/gitlab/quick_actions/work_item_actions.rb54
-rw-r--r--lib/gitlab/usage_data_counters/known_events/quickactions.yml2
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/finders/deployments_finder_spec.rb47
-rw-r--r--spec/models/work_item_spec.rb2
-rw-r--r--spec/requests/api/deployments_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb1
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb221
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb10
-rw-r--r--spec/services/git/base_hooks_service_spec.rb36
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb75
-rw-r--r--spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb41
-rw-r--r--spec/workers/clusters/agents/notify_git_push_worker_spec.rb41
39 files changed, 626 insertions, 127 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 4468934a80b..3ce956b7022 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -1472,26 +1472,20 @@ ee/lib/ee/api/entities/project.rb
[Compliance] @gitlab-org/govern/compliance
/app/services/audit_events/build_service.rb
+/ee/app/services/ee/audit_events/build_service.rb
/ee/spec/services/audit_events/custom_audit_event_service_spec.rb
/app/models/audit_event.rb
/app/services/audit_event_service.rb
/app/services/concerns/audit_event_save_type.rb
/app/views/profiles/audit_log.html.haml
-/ee/app/assets/javascripts/audit_events/components/audit_events_app.vue
-/ee/app/assets/javascripts/audit_events/components/audit_events_export_button.vue
-/ee/app/assets/javascripts/audit_events/components/audit_events_filter.vue
-/ee/app/assets/javascripts/audit_events/components/audit_events_log.vue
-/ee/app/assets/javascripts/audit_events/components/audit_events_stream.vue
-/ee/app/assets/javascripts/audit_events/components/audit_events_table.vue
-/ee/app/assets/javascripts/audit_events/components/tokens/shared/
-/ee/app/assets/javascripts/audit_events/init_audit_events.js
+/ee/app/assets/javascripts/audit_events/
/ee/app/controllers/admin/audit_log_reports_controller.rb
/ee/app/controllers/admin/audit_logs_controller.rb
/ee/app/controllers/concerns/audit_events/audit_events_params.rb
/ee/app/controllers/groups/audit_events_controller.rb
/ee/app/controllers/projects/audit_events_controller.rb
/ee/app/finders/audit_event_finder.rb
-/ee/app/graphql/types/audit_events/external_audit_event_destination_type.rb
+/ee/app/graphql/types/audit_events/
/ee/app/helpers/audit_events_helper.rb
/ee/app/helpers/auditor_user_helper.rb
/ee/app/models/audit_events/external_audit_event_destination.rb
@@ -1531,6 +1525,13 @@ ee/lib/ee/api/entities/project.rb
/ee/lib/ee/gitlab/audit/
/lib/gitlab/audit/auditor.rb
/lib/gitlab/audit_json_logger.rb
+/ee/app/graphql/mutations/audit_events/
+/ee/app/models/concerns/audit_events/
+/ee/app/views/projects/audit_events/
+/app/controllers/groups/releases_controller.rb
+/app/controllers/projects/releases/evidences_controller.rb
+/app/workers/releases/create_evidence_worker.rb
+/app/workers/releases/manage_evidence_worker.rb
[Fulfillment::Utilization] @sheldonled @aalakkad @kpalchyk
/ee/app/assets/javascripts/usage_quotas/components/
diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue
index 8382ae8987b..13b64e762af 100644
--- a/app/assets/javascripts/super_sidebar/components/nav_item.vue
+++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue
@@ -146,7 +146,7 @@ export default {
v-if="hasPill"
size="sm"
variant="neutral"
- class="count-pill gl-absolute gl-right-0"
+ :class="{ 'nav-item-badge gl-absolute gl-right-0 gl-top-2': isPinnable }"
>
{{ pillData }}
</gl-badge>
diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss
index a1e26e78fbe..4124f6b46c5 100644
--- a/app/assets/stylesheets/framework/super_sidebar.scss
+++ b/app/assets/stylesheets/framework/super_sidebar.scss
@@ -191,8 +191,8 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4;
&:hover,
&:focus-within {
- .count-pill {
- display: none;
+ .nav-item-badge {
+ opacity: 0;
}
}
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 2cc2c957f21..a56c7410d0f 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :disable_query_limiting, only: [:create_merge_request, :move, :bulk_update]
before_action :check_issues_available!
before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
- before_action :redirect_if_work_item, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
+ before_action :redirect_if_work_item, unless: ->(c) { work_item_redirect_except_actions.include?(c.action_name.to_sym) }
before_action :require_incident_for_incident_routes, only: :show
after_action :log_issue_show, only: :show
@@ -391,6 +391,10 @@ class Projects::IssuesController < Projects::ApplicationController
private
+ def work_item_redirect_except_actions
+ ISSUES_EXCEPT_ACTIONS
+ end
+
def render_by_create_result_error(result)
Gitlab::AppLogger.warn(
message: 'Cannot create issue',
diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb
index 2a9eb3b9b22..316dffcb3b2 100644
--- a/app/finders/deployments_finder.rb
+++ b/app/finders/deployments_finder.rb
@@ -64,7 +64,15 @@ class DeploymentsFinder
Gitlab::ErrorTracking.log_exception(error)
- raise error if Feature.enabled?(:deployments_raise_updated_at_inefficient_error)
+ # We are adding a Feature Flag to introduce the breaking change indicated in
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/328500
+ # We are also adding a way to override this flag for special case users that
+ # are running into large volume of errors when the flag is enabled.
+ # These Feature Flags must be removed by 16.1
+ if Feature.enabled?(:deployments_raise_updated_at_inefficient_error) &&
+ Feature.disabled?(:deployments_raise_updated_at_inefficient_error_override, params[:project])
+ raise error
+ end
end
if filter_by_finished_at? && !order_by_finished_at?
diff --git a/app/models/work_item.rb b/app/models/work_item.rb
index 11b83528a79..4724d644318 100644
--- a/app/models/work_item.rb
+++ b/app/models/work_item.rb
@@ -4,7 +4,7 @@ class WorkItem < Issue
include Gitlab::Utils::StrongMemoize
COMMON_QUICK_ACTIONS_COMMANDS = [
- :title, :reopen, :close, :cc, :tableflip, :shrug
+ :title, :reopen, :close, :cc, :tableflip, :shrug, :type
].freeze
self.table_name = 'issues'
@@ -104,7 +104,7 @@ class WorkItem < Issue
# Widgets have a set of quick action params that they must process.
# Map them to widget_params so they can be picked up by widget services.
def transform_quick_action_params(command_params)
- common_params = command_params.deep_dup
+ common_params = command_params.dup
widget_params = {}
work_item_type.widgets
diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb
index dfbb37cf0dc..1a5c8d0dccf 100644
--- a/app/services/ci/pipelines/add_job_service.rb
+++ b/app/services/ci/pipelines/add_job_service.rb
@@ -18,6 +18,12 @@ module Ci
in_lock("ci:pipelines:#{pipeline.id}:add-job", ttl: LOCK_TIMEOUT, sleep_sec: LOCK_SLEEP, retries: LOCK_RETRIES) do
Ci::Pipeline.transaction do
+ # This is used to reduce the deadlocks when partitioning `ci_builds`
+ # since inserting into this table requires locks on all foreign keys
+ # and we need to lock all the tables in a specific order for the
+ # migration to succeed.
+ Ci::Pipeline.connection.execute('LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;')
+
yield(job)
job.update_older_statuses_retried!
diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb
index 7158116fde1..acf54dec51b 100644
--- a/app/services/git/base_hooks_service.rb
+++ b/app/services/git/base_hooks_service.rb
@@ -15,6 +15,7 @@ module Git
# Not a hook, but it needs access to the list of changed commits
enqueue_invalidate_cache
+ enqueue_notify_kas
success
end
@@ -77,6 +78,13 @@ module Git
ProjectCacheWorker.perform_async(project.id, file_types, [], false)
end
+ def enqueue_notify_kas
+ return unless Gitlab::Kas.enabled?
+ return unless Feature.enabled?(:notify_kas_on_git_push, project)
+
+ Clusters::Agents::NotifyGitPushWorker.perform_async(project.id)
+ end
+
def pipeline_params
strong_memoize(:pipeline_params) do
{
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index e1646cbf643..b5f6bff756b 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -11,6 +11,7 @@ module QuickActions
include Gitlab::QuickActions::CommitActions
include Gitlab::QuickActions::CommonActions
include Gitlab::QuickActions::RelateActions
+ include Gitlab::QuickActions::WorkItemActions
attr_reader :quick_action_target
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 0efcecae299..e9965ff0027 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -138,6 +138,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: cluster_agent:clusters_agents_notify_git_push
+ :worker_name: Clusters::Agents::NotifyGitPushWorker
+ :feature_category: :deployment_management
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: container_repository:cleanup_container_repository
:worker_name: CleanupContainerRepositoryWorker
:feature_category: :container_registry
diff --git a/app/workers/clusters/agents/notify_git_push_worker.rb b/app/workers/clusters/agents/notify_git_push_worker.rb
new file mode 100644
index 00000000000..d2994bb9144
--- /dev/null
+++ b/app/workers/clusters/agents/notify_git_push_worker.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class NotifyGitPushWorker
+ include ApplicationWorker
+ include ClusterAgentQueue
+
+ deduplicate :until_executed, including_scheduled: true
+ idempotent!
+
+ urgency :low
+ data_consistency :delayed
+
+ def perform(project_id)
+ return unless project = ::Project.find_by_id(project_id)
+ return unless Feature.enabled?(:notify_kas_on_git_push, project)
+
+ Gitlab::Kas::Client.new.send_git_push_event(project: project)
+ end
+ end
+ end
+end
diff --git a/config/feature_flags/development/ci_include_components.yml b/config/feature_flags/development/ci_include_components.yml
index b61863e9675..262493bc0cc 100644
--- a/config/feature_flags/development/ci_include_components.yml
+++ b/config/feature_flags/development/ci_include_components.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/39064
milestone: '15.9'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/deployments_raise_updated_at_inefficient_error.yml b/config/feature_flags/development/deployments_raise_updated_at_inefficient_error.yml
index f1c388f6113..25db63e93cf 100644
--- a/config/feature_flags/development/deployments_raise_updated_at_inefficient_error.yml
+++ b/config/feature_flags/development/deployments_raise_updated_at_inefficient_error.yml
@@ -1,8 +1,8 @@
---
name: deployments_raise_updated_at_inefficient_error
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116656
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/328500
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409584
milestone: '15.11'
type: development
group: group::environments
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/deployments_raise_updated_at_inefficient_error_override.yml b/config/feature_flags/development/deployments_raise_updated_at_inefficient_error_override.yml
new file mode 100644
index 00000000000..80b06d9a720
--- /dev/null
+++ b/config/feature_flags/development/deployments_raise_updated_at_inefficient_error_override.yml
@@ -0,0 +1,8 @@
+---
+name: deployments_raise_updated_at_inefficient_error_override
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120066
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/409584
+milestone: '16.0'
+type: development
+group: group::environments
+default_enabled: false
diff --git a/config/feature_flags/development/notify_kas_on_git_push.yml b/config/feature_flags/development/notify_kas_on_git_push.yml
new file mode 100644
index 00000000000..df90a4d1942
--- /dev/null
+++ b/config/feature_flags/development/notify_kas_on_git_push.yml
@@ -0,0 +1,8 @@
+---
+name: notify_kas_on_git_push
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119168
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/410429
+milestone: '16.0'
+type: development
+group: group::environments
+default_enabled: false
diff --git a/config/metrics/counts_28d/20210216181147_service_desk_enabled_projects.yml b/config/metrics/counts_28d/20210216181147_service_desk_enabled_projects.yml
index 85372300fdb..f45f7be3022 100644
--- a/config/metrics/counts_28d/20210216181147_service_desk_enabled_projects.yml
+++ b/config/metrics/counts_28d/20210216181147_service_desk_enabled_projects.yml
@@ -2,9 +2,9 @@
data_category: optional
key_path: usage_activity_by_stage_monthly.plan.service_desk_enabled_projects
description: Count creator ids from projects with service desk enabled
-product_section: dev
-product_stage: plan
-product_group: product_planning
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: 28d
diff --git a/config/metrics/counts_28d/20210216181148_service_desk_issues.yml b/config/metrics/counts_28d/20210216181148_service_desk_issues.yml
index ae780f35bb2..302dc04e16a 100644
--- a/config/metrics/counts_28d/20210216181148_service_desk_issues.yml
+++ b/config/metrics/counts_28d/20210216181148_service_desk_issues.yml
@@ -2,9 +2,9 @@
data_category: operational
key_path: usage_activity_by_stage_monthly.plan.service_desk_issues
description: Count of service desk issues
-product_section: dev
-product_stage: plan
-product_group: plan
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: 28d
diff --git a/config/metrics/counts_28d/20230420160442_i_quickactions_type_monthly.yml b/config/metrics/counts_28d/20230420160442_i_quickactions_type_monthly.yml
new file mode 100644
index 00000000000..f18f890904e
--- /dev/null
+++ b/config/metrics/counts_28d/20230420160442_i_quickactions_type_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.quickactions.i_quickactions_type_monthly
+description: Count of MAU using the `/type` quick action
+product_section: dev
+product_stage: plan
+product_group: product_planning
+value_type: number
+status: active
+milestone: "16.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118790
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_quickactions_type
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20230420160441_i_quickactions_type_weekly.yml b/config/metrics/counts_7d/20230420160441_i_quickactions_type_weekly.yml
new file mode 100644
index 00000000000..0e7e540885c
--- /dev/null
+++ b/config/metrics/counts_7d/20230420160441_i_quickactions_type_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.quickactions.i_quickactions_type_weekly
+description: Count of MAU using the `/type` quick action
+product_section: dev
+product_stage: plan
+product_group: product_planning
+value_type: number
+status: active
+milestone: "16.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118790
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_quickactions_type
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_all/20210216175024_service_desk_enabled_projects.yml b/config/metrics/counts_all/20210216175024_service_desk_enabled_projects.yml
index 815d440b63c..f81fb42db96 100644
--- a/config/metrics/counts_all/20210216175024_service_desk_enabled_projects.yml
+++ b/config/metrics/counts_all/20210216175024_service_desk_enabled_projects.yml
@@ -2,9 +2,9 @@
data_category: optional
key_path: counts.service_desk_enabled_projects
description: Count of service desk enabled projects
-product_section: dev
-product_stage: plan
-product_group: certify
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: all
diff --git a/config/metrics/counts_all/20210216175026_service_desk_issues.yml b/config/metrics/counts_all/20210216175026_service_desk_issues.yml
index 03761796cd4..3106b6bf7bb 100644
--- a/config/metrics/counts_all/20210216175026_service_desk_issues.yml
+++ b/config/metrics/counts_all/20210216175026_service_desk_issues.yml
@@ -2,9 +2,9 @@
data_category: operational
key_path: counts.service_desk_issues
description: Count of service desk issues
-product_section: dev
-product_stage: plan
-product_group: certify
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: all
diff --git a/config/metrics/counts_all/20210216181122_service_desk_enabled_projects.yml b/config/metrics/counts_all/20210216181122_service_desk_enabled_projects.yml
index 9b5ad936dac..30318cd2d3c 100644
--- a/config/metrics/counts_all/20210216181122_service_desk_enabled_projects.yml
+++ b/config/metrics/counts_all/20210216181122_service_desk_enabled_projects.yml
@@ -2,9 +2,9 @@
data_category: optional
key_path: usage_activity_by_stage.plan.service_desk_enabled_projects
description: Count creator ids from projects with service desk enabled
-product_section: dev
-product_stage: plan
-product_group: product_planning
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: all
diff --git a/config/metrics/counts_all/20210216181124_service_desk_issues.yml b/config/metrics/counts_all/20210216181124_service_desk_issues.yml
index 43b24581052..c2b27567ff1 100644
--- a/config/metrics/counts_all/20210216181124_service_desk_issues.yml
+++ b/config/metrics/counts_all/20210216181124_service_desk_issues.yml
@@ -2,9 +2,9 @@
data_category: optional
key_path: usage_activity_by_stage.plan.service_desk_issues
description: Count of service desk issues
-product_section: dev
-product_stage: plan
-product_group: product_planning
+product_section: ops
+product_stage: monitor
+product_group: respond
value_type: number
status: active
time_frame: all
diff --git a/doc/development/snowplow/review_guidelines.md b/doc/development/snowplow/review_guidelines.md
index 2cf13385179..5a4310c1cde 100644
--- a/doc/development/snowplow/review_guidelines.md
+++ b/doc/development/snowplow/review_guidelines.md
@@ -31,13 +31,13 @@ events or touches Snowplow related files.
[Snowplow Micro](implementation.md#test-backend-events-with-snowplow-micro) good events
`GET http://localhost:9090/micro/good` (it might be a good idea
to reset with `GET http://localhost:9090/micro/reset` first).
-- Update the [Event Dictionary](event_dictionary_guide.md).
+- Add or update the event definition file according to the [Event Dictionary Guide](event_dictionary_guide.md).
#### The Product Intelligence **reviewer** should
- Check that the [event schema](index.md#event-schema) is correct.
- Check the [usage recommendations](implementation.md#usage-recommendations).
-- Check that the [Event Dictionary](event_dictionary_guide.md) is up-to-date.
+- Check that an event definition file was created or updated in accordance with the [Event Dictionary Guide](event_dictionary_guide.md).
- If needed, check that the events are firing locally using one of the
[testing tools](implementation.md#develop-and-test-snowplow) available.
- Approve the MR, and relabel the MR with `~"product intelligence::approved"`.
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 83eef592eda..d6aae38df2b 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -337,7 +337,7 @@ To remain signed in indefinitely, select the **Remember me** checkbox on the Git
You remain signed in because, although the server sets a session time of one week, your browser stores a secure token
that enables automatic reauthentication.
-GitLab administrators can [turn off the **Remember me** setting](../admin_area/settings/account_and_limit_settings.md) for environments
+GitLab administrators can [turn off the **Remember me** setting](../admin_area/settings/account_and_limit_settings.md#session-duration) for environments
that require sessions to expire periodically for security or compliance purposes.
### Cookies used for sign-in
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 38b6bb44b48..777d7119ac6 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -160,6 +160,7 @@ The following quick actions can be applied through the description field when ed
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** Yes | **{dotted-circle}** Yes | Clear [health status](issues/managing_issues.md#health-status).
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, and `2`.
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight.
+| `/type` | **{check-circle}** Yes | **{dotted-circle}** Yes | **{dotted-circle}** Yes | Converts work item to specified type. Available options for `<type>` include `Issue`, `Task`, `Objective` and `Key Result`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385227) in GitLab 16.0 [with a flag](../../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
## Commit messages
diff --git a/lib/gitlab/quick_actions/work_item_actions.rb b/lib/gitlab/quick_actions/work_item_actions.rb
new file mode 100644
index 00000000000..fa43308c9e2
--- /dev/null
+++ b/lib/gitlab/quick_actions/work_item_actions.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module QuickActions
+ module WorkItemActions
+ extend ActiveSupport::Concern
+ include Gitlab::QuickActions::Dsl
+
+ included do
+ desc { _('Change work item type') }
+ explanation do |target_type|
+ format(_("Converts work item to %{type}. Widgets not supported in new type are removed."), type: target_type)
+ end
+ types WorkItem
+ condition do
+ quick_action_target&.project&.work_items_mvc_2_feature_flag_enabled?
+ end
+ params 'Task | Objective | Key Result | Issue'
+ command :type do |type_name|
+ work_item_type = ::WorkItems::Type.find_by_name(type_name)
+ errors = validate_type(work_item_type)
+
+ if errors.present?
+ @execution_message[:type] = errors
+ else
+ @updates[:issue_type] = work_item_type.base_type
+ @updates[:work_item_type] = work_item_type
+ @execution_message[:type] = _('Type changed successfully.')
+ end
+ end
+ end
+
+ private
+
+ def validate_type(type)
+ return type_error(:not_found) unless type.present?
+ return type_error(:same_type) if quick_action_target.work_item_type == type
+ return type_error(:forbidden) unless current_user.can?(:"create_#{type.base_type}", quick_action_target)
+
+ nil
+ end
+
+ def type_error(reason)
+ message = {
+ not_found: 'Provided type is not supported',
+ same_type: 'Types are the same',
+ forbidden: 'You have insufficient permissions'
+ }.freeze
+
+ format(_("Failed to convert this work item: %{reason}."), { reason: message[reason] })
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/known_events/quickactions.yml b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
index ee5fa29c0c3..2521331fee3 100644
--- a/lib/gitlab/usage_data_counters/known_events/quickactions.yml
+++ b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
@@ -127,3 +127,5 @@
aggregation: weekly
- name: i_quickactions_remove_contacts
aggregation: weekly
+- name: i_quickactions_type
+ aggregation: weekly
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3514adeac4a..3b426ca7f89 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8889,6 +8889,9 @@ msgstr ""
msgid "Change title"
msgstr ""
+msgid "Change work item type"
+msgstr ""
+
msgid "Change your password"
msgstr ""
@@ -12199,6 +12202,9 @@ msgstr ""
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
+msgid "Converts work item to %{type}. Widgets not supported in new type are removed."
+msgstr ""
+
msgid "Cookie domain"
msgstr ""
@@ -18147,6 +18153,9 @@ msgstr ""
msgid "Failed to clone this issue: wrong parameters."
msgstr ""
+msgid "Failed to convert this work item: %{reason}."
+msgstr ""
+
msgid "Failed to create a branch for this issue. Please try again."
msgstr ""
@@ -47429,6 +47438,9 @@ msgstr ""
msgid "Type"
msgstr ""
+msgid "Type changed successfully."
+msgstr ""
+
msgid "Type to search"
msgstr ""
diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb
index be7e9a84991..86b6070a368 100644
--- a/spec/finders/deployments_finder_spec.rb
+++ b/spec/finders/deployments_finder_spec.rb
@@ -16,16 +16,6 @@ RSpec.describe DeploymentsFinder do
end
end
- context 'when updated_at filter and id sorting' do
- let(:params) { { updated_before: 1.day.ago, order_by: :id } }
-
- it 'raises an error' do
- expect { subject }.to raise_error(
- described_class::InefficientQueryError,
- '`updated_at` filter requires `updated_at` sort')
- end
- end
-
context 'when finished_at filter and id sorting' do
let(:params) { { finished_before: 1.day.ago, order_by: :id } }
@@ -262,11 +252,7 @@ RSpec.describe DeploymentsFinder do
describe 'enforce sorting to `updated_at` sorting' do
let(:params) { { **base_params, updated_before: 1.day.ago, order_by: 'id', sort: 'asc' } }
- it 'raises an error' do
- expect { subject }.to raise_error(DeploymentsFinder::InefficientQueryError)
- end
-
- context 'when deployments_raise_updated_at_inefficient_error is disabled' do
+ context 'when the deployments_raise_updated_at_inefficient_error FF is disabled' do
before do
stub_feature_flags(deployments_raise_updated_at_inefficient_error: false)
end
@@ -280,6 +266,37 @@ RSpec.describe DeploymentsFinder do
expect(subject.order_values.second.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql)
end
end
+
+ context 'when the deployments_raise_updated_at_inefficient_error FF is enabled' do
+ before do
+ stub_feature_flags(deployments_raise_updated_at_inefficient_error: true)
+ end
+
+ context 'when the flag is overridden' do
+ before do
+ stub_feature_flags(deployments_raise_updated_at_inefficient_error_override: true)
+ end
+
+ it 'sorts by only one column' do
+ expect(subject.order_values.size).to eq(2)
+ end
+
+ it 'sorts by `updated_at`' do
+ expect(subject.order_values.first.to_sql).to eq(Deployment.arel_table[:updated_at].asc.to_sql)
+ expect(subject.order_values.second.to_sql).to eq(Deployment.arel_table[:id].asc.to_sql)
+ end
+ end
+
+ context 'when the flag is not overridden' do
+ before do
+ stub_feature_flags(deployments_raise_updated_at_inefficient_error_override: false)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(DeploymentsFinder::InefficientQueryError)
+ end
+ end
+ end
end
context 'when filtering by finished time' do
diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb
index 24920421ae6..5a525d83c3b 100644
--- a/spec/models/work_item_spec.rb
+++ b/spec/models/work_item_spec.rb
@@ -132,7 +132,7 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
subject { work_item.supported_quick_action_commands }
it 'returns quick action commands supported for all work items' do
- is_expected.to include(:title, :reopen, :close, :cc, :tableflip, :shrug)
+ is_expected.to include(:title, :reopen, :close, :cc, :tableflip, :shrug, :type)
end
context 'when work item supports the assignee widget' do
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 692b4c76ea3..3ca54cd40d0 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -47,6 +47,10 @@ RSpec.describe API::Deployments, feature_category: :continuous_delivery do
end
context 'when forbidden order_by is specified' do
+ before do
+ stub_feature_flags(deployments_raise_updated_at_inefficient_error_override: false)
+ end
+
it 'returns an error' do
perform_request({ updated_before: 30.minutes.ago, updated_after: 90.minutes.ago, order_by: :id })
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
index 2a0b5f291dc..e6feba059c4 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -143,6 +143,7 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do
it_behaves_like 'work item does not support assignee widget updates via quick actions'
it_behaves_like 'work item supports start and due date widget updates via quick actions'
it_behaves_like 'work item does not support start and due date widget updates via quick actions'
+ it_behaves_like 'work item supports type change via quick actions'
end
end
end
diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
index 781cb50734c..ce1c2c01faa 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -17,11 +17,11 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let(:input) { { 'stateEvent' => work_item_event, 'title' => 'updated title' } }
let(:fields) do
<<~FIELDS
- workItem {
- state
- title
- }
- errors
+ workItem {
+ state
+ title
+ }
+ errors
FIELDS
end
@@ -82,10 +82,10 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'when updating confidentiality' do
let(:fields) do
<<~FIELDS
- workItem {
- confidential
- }
- errors
+ workItem {
+ confidential
+ }
+ errors
FIELDS
end
@@ -127,18 +127,18 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'with description widget input' do
let(:fields) do
<<~FIELDS
- workItem {
- title
- description
- state
- widgets {
- type
- ... on WorkItemWidgetDescription {
- description
+ workItem {
+ title
+ description
+ state
+ widgets {
+ type
+ ... on WorkItemWidgetDescription {
+ description
+ }
}
}
- }
- errors
+ errors
FIELDS
end
@@ -446,25 +446,25 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let(:widgets_response) { mutation_response['workItem']['widgets'] }
let(:fields) do
<<~FIELDS
- workItem {
- description
- widgets {
- type
- ... on WorkItemWidgetHierarchy {
- parent {
- id
- }
- children {
- edges {
- node {
- id
+ workItem {
+ description
+ widgets {
+ type
+ ... on WorkItemWidgetHierarchy {
+ parent {
+ id
+ }
+ children {
+ edges {
+ node {
+ id
+ }
}
}
}
}
}
- }
- errors
+ errors
FIELDS
end
@@ -741,23 +741,29 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'when updating assignees' do
let(:fields) do
<<~FIELDS
- workItem {
- widgets {
- type
- ... on WorkItemWidgetAssignees {
- assignees {
- nodes {
- id
- username
+ workItem {
+ title
+ workItemType { name }
+ widgets {
+ type
+ ... on WorkItemWidgetAssignees {
+ assignees {
+ nodes {
+ id
+ username
+ }
}
}
- }
- ... on WorkItemWidgetDescription {
- description
+ ... on WorkItemWidgetDescription {
+ description
+ }
+ ... on WorkItemWidgetStartAndDueDate {
+ startDate
+ dueDate
+ }
}
}
- }
- errors
+ errors
FIELDS
end
@@ -830,6 +836,79 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
)
end
end
+
+ context 'when changing work item type' do
+ let_it_be(:work_item) { create(:work_item, :task, project: project) }
+ let(:description) { "/type Issue" }
+
+ let(:input) { { 'descriptionWidget' => { 'description' => description } } }
+
+ context 'with multiple commands' do
+ let_it_be(:work_item) { create(:work_item, :task, project: project) }
+
+ let(:description) { "Updating work item\n/type Issue\n/due tomorrow\n/title Foo" }
+
+ it 'updates the work item type and other attributes' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to change { work_item.work_item_type.base_type }.from('task').to('issue')
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['workItem']['workItemType']['name']).to eq('Issue')
+ expect(mutation_response['workItem']['title']).to eq('Foo')
+ expect(mutation_response['workItem']['widgets']).to include(
+ 'type' => 'START_AND_DUE_DATE',
+ 'dueDate' => Date.tomorrow.strftime('%Y-%m-%d'),
+ 'startDate' => nil
+ )
+ end
+ end
+
+ context 'when conversion is not permitted' do
+ let_it_be(:issue) { create(:work_item, project: project) }
+ let_it_be(:link) { create(:parent_link, work_item_parent: issue, work_item: work_item) }
+
+ let(:error_msg) { 'Work item type cannot be changed to Issue with Issue as parent type.' }
+
+ it 'does not update the work item type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.not_to change { work_item.work_item_type.base_type }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors']).to include(error_msg)
+ end
+ end
+
+ context 'when new type does not support a widget' do
+ before do
+ work_item.update!(start_date: Date.current, due_date: Date.tomorrow)
+ WorkItems::Type.default_by_type(:issue).widget_definitions
+ .find_by_widget_type(:start_and_due_date).update!(disabled: true)
+ end
+
+ it 'updates the work item type and clear widget attributes' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ work_item.reload
+ end.to change { work_item.work_item_type.base_type }.from('task').to('issue')
+ .and change { work_item.start_date }.to(nil)
+ .and change { work_item.start_date }.to(nil)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['workItem']['workItemType']['name']).to eq('Issue')
+ expect(mutation_response['workItem']['widgets']).to include(
+ {
+ 'type' => 'START_AND_DUE_DATE',
+ 'startDate' => nil,
+ 'dueDate' => nil
+ }
+ )
+ end
+ end
+ end
end
context 'when the work item type does not support the assignees widget' do
@@ -868,17 +947,17 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let(:fields) do
<<~FIELDS
- workItem {
- widgets {
- type
- ... on WorkItemWidgetMilestone {
- milestone {
- id
+ workItem {
+ widgets {
+ type
+ ... on WorkItemWidgetMilestone {
+ milestone {
+ id
+ }
}
}
}
- }
- errors
+ errors
FIELDS
end
@@ -951,15 +1030,15 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let(:fields) do
<<~FIELDS
- workItem {
- widgets {
- type
- ... on WorkItemWidgetNotifications {
- subscribed
+ workItem {
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
}
}
- }
- errors
+ errors
FIELDS
end
@@ -1084,20 +1163,20 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
let(:fields) do
<<~FIELDS
- workItem {
- widgets {
- type
- ... on WorkItemWidgetCurrentUserTodos {
- currentUserTodos {
- nodes {
- id
- state
+ workItem {
+ widgets {
+ type
+ ... on WorkItemWidgetCurrentUserTodos {
+ currentUserTodos {
+ nodes {
+ id
+ state
+ }
}
}
}
}
- }
- errors
+ errors
FIELDS
end
diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb
index 9fb1d6933c6..6380a6a5ec3 100644
--- a/spec/services/ci/pipelines/add_job_service_spec.rb
+++ b/spec/services/ci/pipelines/add_job_service_spec.rb
@@ -86,5 +86,15 @@ RSpec.describe Ci::Pipelines::AddJobService, feature_category: :continuous_integ
expect(execute.payload[:job]).to eq(job)
end
end
+
+ it 'locks pipelines and stages before persisting builds', :aggregate_failures do
+ expect(job).not_to be_persisted
+
+ recorder = ActiveRecord::QueryRecorder.new(skip_cached: false) { execute }
+ entries = recorder.log.select { |query| query.match(/LOCK|INSERT INTO ".{0,2}ci_builds"/) }
+
+ expect(entries.size).to eq(2)
+ expect(entries.first).to match(/LOCK "ci_pipelines", "ci_stages" IN ROW SHARE MODE;/)
+ end
end
end
diff --git a/spec/services/git/base_hooks_service_spec.rb b/spec/services/git/base_hooks_service_spec.rb
index 9d49943ccfb..8a686a19c4c 100644
--- a/spec/services/git/base_hooks_service_spec.rb
+++ b/spec/services/git/base_hooks_service_spec.rb
@@ -325,4 +325,40 @@ RSpec.describe Git::BaseHooksService, feature_category: :source_code_management
end
end
end
+
+ describe 'notifying KAS' do
+ let(:kas_enabled) { true }
+
+ before do
+ allow(Gitlab::Kas).to receive(:enabled?).and_return(kas_enabled)
+ end
+
+ it 'enqueues the notification worker' do
+ expect(Clusters::Agents::NotifyGitPushWorker).to receive(:perform_async).with(project.id).once
+
+ subject.execute
+ end
+
+ context 'when KAS is disabled' do
+ let(:kas_enabled) { false }
+
+ it do
+ expect(Clusters::Agents::NotifyGitPushWorker).not_to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+
+ context 'when :notify_kas_on_git_push feature flag is disabled' do
+ before do
+ stub_feature_flags(notify_kas_on_git_push: false)
+ end
+
+ it do
+ expect(Clusters::Agents::NotifyGitPushWorker).not_to receive(:perform_async)
+
+ subject.execute
+ end
+ end
+ end
end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index b07aa7cc6c9..30cfb92ba06 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -2507,6 +2507,55 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
expect(message).to eq("Added ~\"Bug\" label.")
end
end
+
+ describe 'type command' do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:work_item) { create(:work_item, project: project) }
+
+ let(:command) { '/type Task' }
+
+ context 'when user has sufficient permissions to create new type' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :create_task, work_item).and_return(true)
+ end
+
+ it 'populates :issue_type: and :work_item_type' do
+ _, updates, message = service.execute(command, work_item)
+
+ expect(message).to eq(_('Type changed successfully.'))
+ expect(updates).to eq({ issue_type: 'task', work_item_type: WorkItems::Type.default_by_type(:task) })
+ end
+
+ it 'returns error with an invalid type' do
+ _, updates, message = service.execute('/type foo', work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: Provided type is not supported."))
+ expect(updates).to eq({})
+ end
+
+ it 'returns error with same type' do
+ _, updates, message = service.execute('/type Issue', work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: Types are the same."))
+ expect(updates).to eq({})
+ end
+ end
+
+ context 'when user has insufficient permissions to create new type' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(current_user, :create_task, work_item).and_return(false)
+ end
+
+ it 'returns error' do
+ _, updates, message = service.execute(command, work_item)
+
+ expect(message).to eq(_("Failed to convert this work item: You have insufficient permissions."))
+ expect(updates).to eq({})
+ end
+ end
+ end
end
describe '#explain' do
@@ -2965,6 +3014,32 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
expect(explanations).to eq(['Closes this issue.'])
end
end
+
+ describe 'type command' do
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:work_item) { create(:work_item, :task, project: project) }
+
+ let(:command) { '/type Issue' }
+
+ it 'has command available' do
+ _, explanations = service.explain(command, work_item)
+
+ expect(explanations)
+ .to contain_exactly("Converts work item to Issue. Widgets not supported in new type are removed.")
+ end
+
+ context 'when feature flag work_items_mvc_2 is disabled' do
+ before do
+ stub_feature_flags(work_items_mvc_2: false)
+ end
+
+ it 'does not have the command available' do
+ _, explanations = service.explain(command, work_item)
+
+ expect(explanations).to be_empty
+ end
+ end
+ end
end
describe '#available_commands' do
diff --git a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb
index 5f4e7e5d4e7..52908c5b6df 100644
--- a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb
@@ -152,3 +152,44 @@ RSpec.shared_examples 'work item does not support start and due date widget upda
end.not_to change { noteable.due_date }
end
end
+
+RSpec.shared_examples 'work item supports type change via quick actions' do
+ let_it_be(:assignee) { create(:user) }
+ let_it_be(:task_type) { WorkItems::Type.default_by_type(:task) }
+
+ let(:body) { "Updating type.\n/type Issue" }
+
+ before do
+ noteable.update!(work_item_type: task_type, issue_type: task_type.base_type)
+ end
+
+ it 'updates type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.work_item_type.base_type }.from('task').to('issue')
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ context 'when quick command for unsupported widget is present' do
+ let(:body) { "\n/type Issue\n/assign @#{assignee.username}" }
+
+ before do
+ WorkItems::Type.default_by_type(:issue).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: true)
+ end
+
+ it 'updates only type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.work_item_type.base_type }.from('task').to('issue')
+ .and change { noteable.assignees }.to([])
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors'])
+ .to include("Commands only Type changed successfully. Assigned @#{assignee.username}.")
+ end
+ end
+end
diff --git a/spec/workers/clusters/agents/notify_git_push_worker_spec.rb b/spec/workers/clusters/agents/notify_git_push_worker_spec.rb
new file mode 100644
index 00000000000..561a66b86e9
--- /dev/null
+++ b/spec/workers/clusters/agents/notify_git_push_worker_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Clusters::Agents::NotifyGitPushWorker, feature_category: :deployment_management do
+ let_it_be(:project) { create(:project) }
+
+ describe '#perform' do
+ let(:project_id) { project.id }
+ let(:kas_client) { instance_double(Gitlab::Kas::Client) }
+
+ subject { described_class.new.perform(project_id) }
+
+ it 'calls the deletion service' do
+ expect(Gitlab::Kas::Client).to receive(:new).and_return(kas_client)
+ expect(kas_client).to receive(:send_git_push_event).with(project: project)
+
+ subject
+ end
+
+ context 'when the project no longer exists' do
+ let(:project_id) { -1 }
+
+ it 'completes without raising an error' do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context 'when the :notify_kas_on_git_push feature flag is disabled' do
+ before do
+ stub_feature_flags(notify_kas_on_git_push: false)
+ end
+
+ it 'does not notify KAS' do
+ expect(Gitlab::Kas::Client).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end