diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 71d767c3b95..044b1103086 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -6,10 +6,12 @@ import {
GlSafeHtmlDirective as SafeHtml,
} from '@gitlab/ui';
import { mapActions } from 'vuex';
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserNameWithStatus from '../../sidebar/components/assignees/user_name_with_status.vue';
+import { NOTEABLE_TYPE_MAPPING } from '../constants';
+
export default {
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
components: {
@@ -45,6 +47,11 @@ export default {
required: false,
default: null,
},
+ noteableType: {
+ type: String,
+ required: false,
+ default: '',
+ },
includeToggle: {
type: Boolean,
required: false,
@@ -103,6 +110,15 @@ export default {
authorName() {
return this.author.name;
},
+ noteConfidentialityTooltip() {
+ if (
+ this.noteableType === NOTEABLE_TYPE_MAPPING.Issue ||
+ this.noteableType === NOTEABLE_TYPE_MAPPING.MergeRequest
+ ) {
+ return s__('Notes|This comment is confidential and only visible to project members');
+ }
+ return s__('Notes|This comment is confidential and only visible to group members');
+ },
},
mounted() {
this.emojiTitle = this.emojiElement ? this.emojiElement.getAttribute('title') : '';
@@ -226,7 +242,7 @@ export default {
data-testid="confidentialIndicator"
name="eye-slash"
:size="16"
- :title="s__('Notes|This comment is confidential and only visible to project members')"
+ :title="noteConfidentialityTooltip"
class="gl-ml-1 gl-text-orange-700 align-middle"
/>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index a271ac91f6e..2290adfdce6 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -432,6 +432,7 @@ export default {
:created-at="note.created_at"
:note-id="note.id"
:is-confidential="note.confidential"
+ :noteable-type="noteableType"
>
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index ef229a2abec..8e2c471a19a 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -32,6 +32,7 @@ class GraphqlController < ApplicationController
before_action :set_user_last_activity
before_action :track_vs_code_usage
before_action :track_jetbrains_usage
+ before_action :track_gitlab_cli_usage
before_action :disable_query_limiting
before_action :limit_query_size
@@ -143,6 +144,11 @@ class GraphqlController < ApplicationController
.track_api_request_when_trackable(user_agent: request.user_agent, user: current_user)
end
+ def track_gitlab_cli_usage
+ Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter
+ .track_api_request_when_trackable(user_agent: request.user_agent, user: current_user)
+ end
+
def execute_multiplex
GitlabSchema.multiplex(multiplex_queries, context: context)
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 03d3dc1b8df..b579322802a 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -10,7 +10,7 @@ class Projects::IssuesController < Projects::ApplicationController
include RecordUserLastActivity
ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze
- SET_ISSUABLES_INDEX_ONLY_ACTIONS = %i[calendar service_desk].freeze
+ SET_ISSUABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:calendar]) { authenticate_sessionless_user!(:ics) }
@@ -22,7 +22,9 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
after_action :log_issue_show, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
- before_action :set_issuables_index, if: ->(c) { SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) }
+ before_action :set_issuables_index, if: ->(c) {
+ SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) && !vue_issues_list?
+ }
# Allow write(create) issue
before_action :authorize_create_issue!, only: [:new, :create]
@@ -71,10 +73,9 @@ class Projects::IssuesController < Projects::ApplicationController
attr_accessor :vulnerability_id
def index
- if html_request? && Feature.enabled?(:vue_issues_list, project&.group, default_enabled: :yaml)
+ if vue_issues_list?
set_sort_order
else
- set_issuables_index
@issues = @issuables
end
@@ -248,6 +249,12 @@ class Projects::IssuesController < Projects::ApplicationController
protected
+ def vue_issues_list?
+ action_name.to_sym == :index &&
+ html_request? &&
+ Feature.enabled?(:vue_issues_list, project&.group, default_enabled: :yaml)
+ end
+
def sorting_field
Issue::SORTING_PREFERENCE_FIELD
end
diff --git a/app/finders/releases/group_releases_finder.rb b/app/finders/releases/group_releases_finder.rb
index d87ba8c0b03..39f44830ff5 100644
--- a/app/finders/releases/group_releases_finder.rb
+++ b/app/finders/releases/group_releases_finder.rb
@@ -6,9 +6,8 @@ module Releases
#
# order_by - only ordering by released_at is supported
# filter by tag - currently not supported
+ # include_subgroups - always true for group releases finder
class GroupReleasesFinder
- include Gitlab::Utils::StrongMemoize
-
attr_reader :parent, :current_user, :params
def initialize(parent, current_user = nil, params = {})
@@ -25,45 +24,26 @@ module Releases
def execute(preload: true)
return Release.none unless Ability.allowed?(current_user, :read_release, parent)
- releases = get_releases(preload: preload)
-
+ releases = get_releases
+ releases.preloaded if preload
paginate_releases(releases)
end
private
- def include_subgroups?
- params.fetch(:include_subgroups, false)
- end
-
- def accessible_projects_scope
- if include_subgroups?
- Project.for_group_and_its_subgroups(parent)
- else
- parent.projects
- end
- end
-
# rubocop: disable CodeReuse/ActiveRecord
- def get_releases(preload: true)
+ def get_releases
Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder.new(
- scope: releases_scope(preload: preload),
- array_scope: accessible_projects_scope.select(:id),
+ scope: releases_scope,
+ array_scope: Project.for_group_and_its_subgroups(parent).select(:id),
array_mapping_scope: -> (project_id_expression) { Release.where(Release.arel_table[:project_id].eq(project_id_expression)) },
finder_query: -> (order_by, id_expression) { Release.where(Release.arel_table[:id].eq(id_expression)) }
)
.execute
end
- def releases_scope(preload: true)
- scope = Release.all
- scope = order_releases(scope)
- scope = scope.preloaded if preload
- scope
- end
-
- def order_releases(scope)
- scope.sort_by_attribute("released_at_#{params[:sort]}").order(id: params[:sort])
+ def releases_scope
+ Release.sort_by_attribute("released_at_#{params[:sort]}").order(id: params[:sort])
end
def paginate_releases(releases)
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 7d4c3b6115f..5c8acc053f4 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -11,7 +11,7 @@
%h5.gl-mt-0
= _('Add an SSH key')
%p.profile-settings-content
- - help_link_start = ''.html_safe % { url: help_page_path('ssh/index.md') }
+ - help_link_start = ''.html_safe % { url: help_page_path('user/ssh.md') }
= _('Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}').html_safe % {help_link_start: help_link_start, help_link_end: ''.html_safe }
= render 'form'
%hr
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml b/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml
new file mode 100644
index 00000000000..898c19a34a9
--- /dev/null
+++ b/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_i_code_review_user_gitlab_cli_api_request
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83464
+rollout_issue_url:
+milestone: '14.10'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/feature_flags/development/user_other_role_details.yml b/config/feature_flags/development/user_other_role_details.yml
index 47666a1d5c5..7c0b417d398 100644
--- a/config/feature_flags/development/user_other_role_details.yml
+++ b/config/feature_flags/development/user_other_role_details.yml
@@ -1,7 +1,7 @@
---
name: user_other_role_details
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45635
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255170
+rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/282
milestone: '13.7'
type: development
group: group::conversion
diff --git a/config/initializers_before_autoloader/004_zeitwerk.rb b/config/initializers_before_autoloader/004_zeitwerk.rb
index 60cc57c3282..ff96a84166a 100644
--- a/config/initializers_before_autoloader/004_zeitwerk.rb
+++ b/config/initializers_before_autoloader/004_zeitwerk.rb
@@ -25,6 +25,7 @@ Rails.autoloaders.each do |autoloader|
'cidr' => 'CIDR',
'cli' => 'CLI',
'dn' => 'DN',
+ 'gitlab_cli_activity_unique_counter' => 'GitLabCliActivityUniqueCounter',
'global_id_type' => 'GlobalIDType',
'global_id_compatibility' => 'GlobalIDCompatibility',
'hll' => 'HLL',
diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml
index aee0e602e7b..004f155864e 100644
--- a/config/metrics/aggregates/code_review.yml
+++ b/config/metrics/aggregates/code_review.yml
@@ -74,6 +74,7 @@
- 'i_code_review_post_merge_submit_revert_modal'
- 'i_code_review_post_merge_submit_cherry_pick_modal'
- 'i_code_review_user_jetbrains_api_request'
+ - 'i_code_review_user_gitlab_cli_api_request'
- name: code_review_category_monthly_active_users
operator: OR
source: redis
@@ -146,3 +147,4 @@
events:
- 'i_code_review_user_vs_code_api_request'
- 'i_code_review_user_jetbrains_api_request'
+ - 'i_code_review_user_gitlab_cli_api_request'
diff --git a/config/metrics/counts_28d/20220322194931_users_gitlab_cli_api_request_monthly.yml b/config/metrics/counts_28d/20220322194931_users_gitlab_cli_api_request_monthly.yml
new file mode 100644
index 00000000000..2d534efba1c
--- /dev/null
+++ b/config/metrics/counts_28d/20220322194931_users_gitlab_cli_api_request_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_gitlab_cli_api_request_monthly
+description: Count of unique users per month who use the GitLab CLI
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: editor_extension
+value_type: number
+status: active
+milestone: "14.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83464
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_gitlab_cli_api_request
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220322194922_users_gitlab_cli_api_request_weekly.yml b/config/metrics/counts_7d/20220322194922_users_gitlab_cli_api_request_weekly.yml
new file mode 100644
index 00000000000..333e84873b4
--- /dev/null
+++ b/config/metrics/counts_7d/20220322194922_users_gitlab_cli_api_request_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_user_gitlab_cli_api_request_weekly
+description: Count of unique users per week who use the GitLab CLI
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: editor_extension
+value_type: number
+status: active
+milestone: "14.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83464
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_gitlab_cli_api_request
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index de115306f1d..098788ee810 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1458,7 +1458,7 @@ Input type: `CreateIssueInput`
WARNING:
**Deprecated** in 14.0.
-Use iterationCreate.
+Manual iteration management is deprecated. Only automatic iteration cadences will be supported in the future.
Input type: `CreateIterationInput`
@@ -1470,7 +1470,7 @@ Input type: `CreateIterationInput`
| `description` | [`String`](#string) | Description of the iteration. |
| `dueDate` | [`String`](#string) | End date of the iteration. |
| `groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
-| `iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
+| `iterationsCadenceId` **{warning-solid}** | [`IterationsCadenceID`](#iterationscadenceid) | **Deprecated:** `iterationCadenceId` is deprecated and will be removed in the future. This argument is ignored, because you can't create an iteration in a specific cadence. In the future only automatic iteration cadences will be allowed. Deprecated in 14.10. |
| `projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
| `startDate` | [`String`](#string) | Start date of the iteration. |
| `title` | [`String`](#string) | Title of the iteration. |
@@ -3213,6 +3213,10 @@ Input type: `IterationCadenceUpdateInput`
### `Mutation.iterationCreate`
+WARNING:
+**Deprecated** in 14.10.
+Manual iteration management is deprecated. Only automatic iteration cadences will be supported in the future.
+
Input type: `iterationCreateInput`
#### Arguments
@@ -3223,7 +3227,7 @@ Input type: `iterationCreateInput`
| `description` | [`String`](#string) | Description of the iteration. |
| `dueDate` | [`String`](#string) | End date of the iteration. |
| `groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
-| `iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
+| `iterationsCadenceId` **{warning-solid}** | [`IterationsCadenceID`](#iterationscadenceid) | **Deprecated:** `iterationCadenceId` is deprecated and will be removed in the future. This argument is ignored, because you can't create an iteration in a specific cadence. In the future only automatic iteration cadences will be allowed. Deprecated in 14.10. |
| `projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
| `startDate` | [`String`](#string) | Start date of the iteration. |
| `title` | [`String`](#string) | Title of the iteration. |
diff --git a/doc/user/group/iterations/index.md b/doc/user/group/iterations/index.md
index 5beef7cb1ba..999d4b15cb0 100644
--- a/doc/user/group/iterations/index.md
+++ b/doc/user/group/iterations/index.md
@@ -12,6 +12,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Moved to GitLab Premium in 13.9.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/221047) in GitLab 14.6. [Feature flag `group_iterations`](https://gitlab.com/gitlab-org/gitlab/-/issues/221047) removed.
+WARNING:
+After [Iteration Cadences](#iteration-cadences) becomes generally available,
+manual iteration scheduling will be [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/356069) in GitLab 15.6.
+To enhance the role of iterations as time boundaries, we will also deprecate the title field.
+
Iterations are a way to track issues over a period of time. This allows teams
to track velocity and volatility metrics. Iterations can be used with [milestones](../../project/milestones/index.md)
for tracking over different time periods.
@@ -28,54 +33,6 @@ In GitLab, iterations are similar to milestones, with a few differences:
- Iterations require both a start and an end date.
- Iteration date ranges cannot overlap.
-## Iteration cadences
-
-> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5077) in GitLab 14.1.
-> - Deployed behind a [feature flag](../../feature_flags.md), disabled by default.
-> - Disabled on GitLab.com.
-> - Not recommended for production use.
-> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-iteration-cadences).
-
-This in-development feature might not be available for your use. There can be
-[risks when enabling features still in development](../../../administration/feature_flags.md#risks-when-enabling-features-still-in-development).
-Refer to this feature's version history for more details.
-
-Iteration cadences automate some common iteration tasks. They can be used to
-automatically create iterations every 1, 2, 3, 4, or 6 weeks. They can also
-be configured to automatically roll over incomplete issues to the next iteration.
-
-With iteration cadences enabled, you must first
-[create an iteration cadence](#create-an-iteration-cadence) before you can
-[create an iteration](#create-an-iteration).
-
-### Create an iteration cadence
-
-Prerequisites:
-
-- You must have at least the Developer role for a group.
-
-To create an iteration cadence:
-
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Issues > Iterations**.
-1. Select **New iteration cadence**.
-1. Fill out required fields, and select **Create iteration cadence**. The cadence list page opens.
-
-### Delete an iteration cadence
-
-Prerequisites:
-
-- You must have at least the Developer role for a group.
-
-Deleting an iteration cadence also deletes all iterations within that cadence.
-
-To delete an iteration cadence:
-
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Issues > Iterations**.
-1. Select the three-dot menu (**{ellipsis_v}**) > **Delete cadence** for the cadence you want to delete.
-1. Select **Delete cadence** in the confirmation modal.
-
## View the iterations list
To view the iterations list, go to **{issues}** **Issues > Iterations**.
@@ -94,8 +51,6 @@ Prerequisites:
- You must have at least the Developer role for a group.
-For manually scheduled iteration cadences, you create and add iterations yourself.
-
To create an iteration:
1. On the top bar, select **Menu > Groups** and find your group.
@@ -153,7 +108,7 @@ The report also shows a breakdown of total issues in an iteration.
Open iteration reports show a summary of completed, unstarted, and in-progress issues.
Closed iteration reports show the total number of issues completed by the due date.
-To view an iteration report, go to the iterations list page and select an iteration's title.
+To view an iteration report, go to the iterations list page and select an iteration's period.
### Iteration burndown and burnup charts
@@ -212,33 +167,61 @@ To group issues by label:
You can also search for labels by typing in the search input.
1. Select any area outside the label dropdown list. The page is now grouped by the selected labels.
-### Enable or disable iteration cadences **(PREMIUM SELF)**
+## Iteration cadences
-Iteration Cadences feature is under development and not ready for production use. It is
-deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can enable it.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5077) in GitLab 14.1.
+> - Deployed behind a [feature flag](../../feature_flags.md), named `iteration_cadences`, disabled by default.
-To enable it:
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available, ask an
+administrator to [enable the feature flag](../../../administration/feature_flags.md) named
+`iteration_cadences` for a root group.
+On GitLab.com, this feature is not available. This feature is not ready for production use.
-```ruby
-Feature.enable(:iteration_cadences)
-```
+Iteration cadences automate iteration scheduling. You can use them to
+automate creating iterations every 1, 2, 3, 4, or 6 weeks. You can also
+configure iteration cadences to automatically roll over incomplete issues to the next iteration.
-To disable it:
+### Create an iteration cadence
-```ruby
-Feature.disable(:iteration_cadences)
-```
+Prerequisites:
-
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Issues > Iterations**.
+1. Select **New iteration cadence**.
+1. Fill out required fields, and select **Create iteration cadence**. The cadence list page opens.
+
+### Delete an iteration cadence
+
+Prerequisites:
+
+- You must have at least the Developer role for a group.
+
+Deleting an iteration cadence also deletes all iterations within that cadence.
+
+To delete an iteration cadence:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Issues > Iterations**.
+1. Select the three-dot menu (**{ellipsis_v}**) > **Delete cadence** for the cadence you want to delete.
+1. Select **Delete cadence** in the confirmation modal.
+
+### Convert manual cadence to use automatic scheduling
+
+WARNING:
+The upgrade is irreversible. After it's done, manual iteration cadences cannot be created.
+
+When you **enable** the iteration cadences feature, all iterations are added
+to a default iteration cadence.
+In this default iteration cadence, you can continue to add, edit, and remove iterations.
+
+To upgrade the iteration cadence to use the automation features:
+
+1. On the top bar, select **Menu > Groups** and find your group.
+1. On the left sidebar, select **Issues > Iterations**.
+1. Select the three-dot menu (**{ellipsis_v}**) > **Edit cadence** for the cadence you want to upgrade.
+1. Fill out required fields, and select **Save changes**.
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 5100ec9ec9d..af0e0a12ae9 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -80,6 +80,10 @@ module API
Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user)
end
+ after do
+ Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user)
+ end
+
# The locale is set to the current user's locale when `current_user` is loaded
after { Gitlab::I18n.use_default_locale }
diff --git a/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter.rb
new file mode 100644
index 00000000000..8a57a0331b8
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module UsageDataCounters
+ module GitLabCliActivityUniqueCounter
+ GITLAB_CLI_API_REQUEST_ACTION = 'i_code_review_user_gitlab_cli_api_request'
+ GITLAB_CLI_USER_AGENT_REGEX = /GitLab\sCLI$/.freeze
+
+ class << self
+ def track_api_request_when_trackable(user_agent:, user:)
+ user_agent&.match?(GITLAB_CLI_USER_AGENT_REGEX) && track_unique_action_by_user(GITLAB_CLI_API_REQUEST_ACTION, user)
+ end
+
+ private
+
+ def track_unique_action_by_user(action, user)
+ return unless user
+
+ track_unique_action(action, user.id)
+ end
+
+ def track_unique_action(action, value)
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event(action, value)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index 42c51ec3921..94801ec0ed6 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -132,6 +132,11 @@
category: code_review
aggregation: weekly
feature_flag: usage_data_i_code_review_user_jetbrains_api_request
+- name: i_code_review_user_gitlab_cli_api_request
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: usage_data_i_code_review_user_gitlab_cli_api_request
- name: i_code_review_user_create_mr_from_issue
redis_slot: code_review
category: code_review
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e67fb96da60..12241764339 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -21107,10 +21107,13 @@ msgstr ""
msgid "Iterations"
msgstr ""
+msgid "Iterations|Add a duration, and number of future iterations in order to convert this cadence to automatic scheduling."
+msgstr ""
+
msgid "Iterations|Add iteration"
msgstr ""
-msgid "Iterations|Automated scheduling"
+msgid "Iterations|All"
msgstr ""
msgid "Iterations|Cadence configuration is invalid."
@@ -21125,12 +21128,6 @@ msgstr ""
msgid "Iterations|Create cadence"
msgstr ""
-msgid "Iterations|Create cadence and start iteration"
-msgstr ""
-
-msgid "Iterations|Create iteration"
-msgstr ""
-
msgid "Iterations|Delete cadence"
msgstr ""
@@ -21140,6 +21137,9 @@ msgstr ""
msgid "Iterations|Delete iteration?"
msgstr ""
+msgid "Iterations|Done"
+msgstr ""
+
msgid "Iterations|Duration"
msgstr ""
@@ -21161,10 +21161,13 @@ msgstr ""
msgid "Iterations|Iteration cadences"
msgstr ""
-msgid "Iterations|Iteration scheduling will be handled automatically"
+msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
msgstr ""
-msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
+msgid "Iterations|Iterations can no longer be scheduled manually. Convert all cadences to automatic scheduling to keep your iterations working as expected."
+msgstr ""
+
+msgid "Iterations|Learn more about automatic scheduling"
msgstr ""
msgid "Iterations|Move incomplete issues to the next iteration"
@@ -21194,10 +21197,16 @@ msgstr ""
msgid "Iterations|Number of future iterations you would like to have scheduled"
msgstr ""
+msgid "Iterations|Open"
+msgstr ""
+
+msgid "Iterations|Requires update"
+msgstr ""
+
msgid "Iterations|Roll over issues"
msgstr ""
-msgid "Iterations|Save cadence"
+msgid "Iterations|Save changes"
msgstr ""
msgid "Iterations|Select duration"
@@ -21209,6 +21218,9 @@ msgstr ""
msgid "Iterations|Select start date"
msgstr ""
+msgid "Iterations|Some of your cadences need to be updated"
+msgstr ""
+
msgid "Iterations|Start date"
msgstr ""
@@ -21221,6 +21233,9 @@ msgstr ""
msgid "Iterations|The start date of your first iteration"
msgstr ""
+msgid "Iterations|This cadence requires an update"
+msgstr ""
+
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -25646,6 +25661,9 @@ msgstr ""
msgid "Notes|This comment has changed since you started editing, please review the %{open_link}updated comment%{close_link} to ensure information is not lost"
msgstr ""
+msgid "Notes|This comment is confidential and only visible to group members"
+msgstr ""
+
msgid "Notes|This comment is confidential and only visible to project members"
msgstr ""
diff --git a/qa/qa/runtime/api/repository_storage_moves.rb b/qa/qa/runtime/api/repository_storage_moves.rb
index c3b2095be32..fb8d70c0836 100644
--- a/qa/qa/runtime/api/repository_storage_moves.rb
+++ b/qa/qa/runtime/api/repository_storage_moves.rb
@@ -24,7 +24,7 @@ module QA
Logger.debug('Getting repository storage moves')
Support::Waiter.wait_until do
- with_paginated_response_body(Request.new(api_client, "/#{resource_name(resource)}_repository_storage_moves", per_page: '100').url) do |page|
+ get(Request.new(api_client, "/#{resource_name(resource)}_repository_storage_moves", per_page: '100').url) do |page|
break true if page.any? { |item| yield item }
end
end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
index 624ddbb68e1..cd1b7730fa9 100644
--- a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
+++ b/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Changing Gitaly repository storage', :requires_admin do
+ describe 'Changing Gitaly repository storage', :requires_admin, except: { job: 'review-qa-*' } do
praefect_manager = Service::PraefectManager.new
shared_examples 'repository storage move' do
@@ -11,12 +11,16 @@ module QA
expect { project.change_repository_storage(destination_storage[:name]) }.not_to raise_error
expect { praefect_manager.verify_storage_move(source_storage, destination_storage, repo_type: :project) }.not_to raise_error
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.file_name = 'new_file'
- push.file_content = '# This is a new file'
- push.commit_message = 'Add new file'
- push.new_branch = false
+ Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ # For a short period of time after migrating, the repository can be 'read only' which may lead to errors
+ # 'The repository is temporarily read-only. Please try again later.'
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add new file'
+ commit.add_files([
+ { file_path: 'new_file', content: '# This is a new file' }
+ ])
+ end
end
expect(project).to have_file('README.md')
@@ -45,7 +49,7 @@ module QA
# Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
# scenario with other tests that aren't considered orchestrated.
# It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
- context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/284645', type: :investigating } do
+ context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828' do
let(:source_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
let(:destination_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
index 2933d580957..3921595204c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Create project badge' do
+ describe 'Create project badge', :reliable do
let(:badge_name) { "project-badge-#{SecureRandom.hex(8)}" }
let(:expected_badge_link_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}" }
let(:expected_badge_image_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}/badges/main/pipeline.svg" }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb
index 2aefa1c39ed..5d0befea1ce 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Personal project permissions' do
+ describe 'Personal project permissions', :reliable do
let!(:owner) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let!(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner) }
diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb
index a9ceb695ba4..07fb568e512 100644
--- a/spec/controllers/graphql_controller_spec.rb
+++ b/spec/controllers/graphql_controller_spec.rb
@@ -135,6 +135,16 @@ RSpec.describe GraphqlController do
post :execute
end
+ it 'calls the track gitlab cli when trackable method' do
+ agent = 'GLab - GitLab CLI'
+ request.env['HTTP_USER_AGENT'] = agent
+
+ expect(Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter)
+ .to receive(:track_api_request_when_trackable).with(user_agent: agent, user: user)
+
+ post :execute
+ end
+
it "assigns username in ApplicationContext" do
post :execute
@@ -220,6 +230,16 @@ RSpec.describe GraphqlController do
subject
end
+
+ it 'calls the track gitlab cli when trackable method' do
+ agent = 'GLab - GitLab CLI'
+ request.env['HTTP_USER_AGENT'] = agent
+
+ expect(Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter)
+ .to receive(:track_api_request_when_trackable).with(user_agent: agent, user: user)
+
+ subject
+ end
end
context 'when user is not logged in' do
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 4e2123c8cc4..70dc710f604 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -142,11 +142,11 @@ RSpec.describe HelpController do
context 'for Markdown formats' do
subject { get :show, params: { path: path }, format: :md }
- let(:path) { 'ssh/index' }
+ let(:path) { 'user/ssh' }
context 'when requested file exists' do
before do
- expect_file_read(File.join(Rails.root, 'doc/ssh/index.md'), content: fixture_file('blockquote_fence_after.md'))
+ expect_file_read(File.join(Rails.root, 'doc/user/ssh.md'), content: fixture_file('blockquote_fence_after.md'))
subject
end
@@ -257,7 +257,7 @@ RSpec.describe HelpController do
it 'always renders not found' do
get :show,
params: {
- path: 'ssh/index'
+ path: 'user/ssh'
},
format: :foo
expect(response).to be_not_found
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 9d3711d8a96..ce0af784cdf 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -148,6 +148,13 @@ RSpec.describe Projects::IssuesController do
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
end
+ it 'redirects to last page when out of bounds on non-html requests' do
+ get :index, params: params.merge(page: last_page + 1), format: 'atom'
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(response).to redirect_to(action: 'index', format: 'atom', page: last_page, state: 'opened')
+ end
+
it 'does not use pagination if disabled' do
allow(controller).to receive(:pagination_disabled?).and_return(true)
diff --git a/spec/finders/releases/group_releases_finder_spec.rb b/spec/finders/releases/group_releases_finder_spec.rb
index b8899a8ee40..5eac6f4fbdc 100644
--- a/spec/finders/releases/group_releases_finder_spec.rb
+++ b/spec/finders/releases/group_releases_finder_spec.rb
@@ -95,8 +95,6 @@ RSpec.describe Releases::GroupReleasesFinder do
end
describe 'with subgroups' do
- let(:params) { { include_subgroups: true } }
-
subject(:releases) { described_class.new(group, user, params).execute(**args) }
context 'with a single-level subgroup' do
@@ -164,22 +162,12 @@ RSpec.describe Releases::GroupReleasesFinder do
end
end
- context 'when the user a guest on the group' do
- before do
- group.add_guest(user)
- end
-
- it 'returns all releases' do
- expect(releases).to match_array([v1_1_1, v1_1_0, v6, v1_0_0, p3])
- end
- end
-
context 'performance testing' do
shared_examples 'avoids N+1 queries' do |query_params = {}|
context 'with subgroups' do
let(:params) { query_params }
- it 'include_subgroups avoids N+1 queries' do
+ it 'subgroups avoids N+1 queries' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
releases
end.count
@@ -196,7 +184,6 @@ RSpec.describe Releases::GroupReleasesFinder do
end
it_behaves_like 'avoids N+1 queries'
- it_behaves_like 'avoids N+1 queries', { simple: true }
end
end
end
diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js
index 4671d33219d..a96d6578e2c 100644
--- a/spec/frontend/notes/components/note_header_spec.js
+++ b/spec/frontend/notes/components/note_header_spec.js
@@ -296,5 +296,13 @@ describe('NoteHeader component', () => {
createComponent({ isConfidential: status });
expect(findConfidentialIndicator().exists()).toBe(status);
});
+
+ it('shows confidential indicator tooltip for project context', () => {
+ createComponent({ isConfidential: true, noteableType: 'issue' });
+
+ expect(findConfidentialIndicator().attributes('title')).toBe(
+ 'This comment is confidential and only visible to project members',
+ );
+ });
});
});
diff --git a/spec/frontend/notes/components/noteable_note_spec.js b/spec/frontend/notes/components/noteable_note_spec.js
index c7115a5911b..385edc59eb6 100644
--- a/spec/frontend/notes/components/noteable_note_spec.js
+++ b/spec/frontend/notes/components/noteable_note_spec.js
@@ -11,6 +11,7 @@ import NoteBody from '~/notes/components/note_body.vue';
import NoteHeader from '~/notes/components/note_header.vue';
import issueNote from '~/notes/components/noteable_note.vue';
import NotesModule from '~/notes/stores/modules';
+import { NOTEABLE_TYPE_MAPPING } from '~/notes/constants';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
@@ -226,6 +227,7 @@ describe('issue_note', () => {
expect(noteHeaderProps.author).toBe(note.author);
expect(noteHeaderProps.createdAt).toBe(note.created_at);
expect(noteHeaderProps.noteId).toBe(note.id);
+ expect(noteHeaderProps.noteableType).toBe(NOTEABLE_TYPE_MAPPING[note.noteable_type]);
});
it('should render note actions', () => {
diff --git a/spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb
new file mode 100644
index 00000000000..d6eb67e5c35
--- /dev/null
+++ b/spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter, :clean_gitlab_redis_shared_state do # rubocop:disable RSpec/FilePath
+ let(:user1) { build(:user, id: 1) }
+ let(:user2) { build(:user, id: 2) }
+ let(:time) { Time.current }
+ let(:action) { described_class::GITLAB_CLI_API_REQUEST_ACTION }
+ let(:user_agent) { { user_agent: 'GLab - GitLab CLI' } }
+
+ context 'when tracking a gitlab cli request' do
+ it_behaves_like 'a request from an extension'
+ end
+end
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 3a264d41dac..83bdcd0b5bb 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -28,7 +28,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/smartystreets/goconvey v1.6.4
github.com/stretchr/testify v1.7.0
- gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc1
+ gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.6.0
gocloud.dev v0.23.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index a565e93bfcc..1cb7418d3c1 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -606,7 +606,7 @@ github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libgit2/git2go v0.0.0-20190104134018-ecaeb7a21d47/go.mod h1:4bKN42efkbNYMZlvDfxGDxzl066GhpvIircZDsm8Y+Y=
github.com/libgit2/git2go/v31 v31.4.12/go.mod h1:c/rkJcBcUFx6wHaT++UwNpKvIsmPNqCeQ/vzO4DrEec=
-github.com/libgit2/git2go/v33 v33.0.6/go.mod h1:KdpqkU+6+++4oHna/MIOgx4GCQ92IPCdpVRMRI80J+4=
+github.com/libgit2/git2go/v33 v33.0.9/go.mod h1:KdpqkU+6+++4oHna/MIOgx4GCQ92IPCdpVRMRI80J+4=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20200305213919-a88bf8de3718/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 h1:YjW+hUb8Fh2S58z4av4t/0cBMK/Q0aP48RocCFsC8yI=
@@ -886,8 +886,8 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
gitlab.com/gitlab-org/gitaly v1.68.0 h1:VlcJs1+PrhW7lqJUU7Fh1q8FMJujmbbivdfde/cwB98=
gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
gitlab.com/gitlab-org/gitaly/v14 v14.0.0-rc1/go.mod h1:4Cz8tOAyueSZX5o6gYum1F/unupaOclxqETPcg4ODvQ=
-gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc1 h1:9vStRdXxcBQ8dHlVnpV28fwLOgyDkSFIpGnPqwzdTvw=
-gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc1/go.mod h1:Xk5pn6IWsejg3z2X6BRczC5QaI97PRF3GU5OrJ5Amkg=
+gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059 h1:X7+3GQIxUpScXpIMCU5+sfpYvZyBIQ3GMlEosP7Jssw=
+gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059/go.mod h1:uX1qhFKBDuPqATlpMcFL2dKDiX8D/tbUg7CYWx7OXt4=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20210720163109-50da611814d2/go.mod h1:QWDYBwuy24qGMandtCngLRPzFgnGPg6LSNoJWPKmJMc=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=