diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-10 09:08:55 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-10 09:08:55 +0000 |
commit | a2344dbf1942dc3919c55b0684d2566368e03852 (patch) | |
tree | 00f92dc8b5e2e2cb732662f5e5e0ffd5f3d2eca0 | |
parent | 213da19cda5309148952ab770e2a9e122fe32e22 (diff) | |
download | gitlab-ce-a2344dbf1942dc3919c55b0684d2566368e03852.tar.gz |
Add latest changes from gitlab-org/gitlab@master
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 |