Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-09 21:10:12 +00:00
parent 7c0e5472c8
commit dc22d7faa2
57 changed files with 792 additions and 281 deletions

View File

@ -391,7 +391,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/features/projects/environments/environments_spec.rb'
- 'ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
- 'ee/spec/features/projects/pipelines/pipeline_spec.rb'
- 'ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- 'ee/spec/frontend/fixtures/search.rb'
- 'ee/spec/graphql/mutations/requirements_management/export_requirements_spec.rb'

View File

@ -1979,7 +1979,6 @@ Layout/LineLength:
- 'ee/spec/features/search/elastic/snippet_search_spec.rb'
- 'ee/spec/features/subscriptions_spec.rb'
- 'ee/spec/features/trial_registrations/company_information_spec.rb'
- 'ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- 'ee/spec/features/users/login_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/enabled_namespaces_finder_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/snapshots_finder_spec.rb'
@ -5159,7 +5158,6 @@ Layout/LineLength:
- 'spec/lib/grafana/validator_spec.rb'
- 'spec/lib/kramdown/kramdown_spec.rb'
- 'spec/lib/kramdown/parser/atlassian_document_format_spec.rb'
- 'spec/lib/learn_gitlab/project_spec.rb'
- 'spec/lib/mattermost/command_spec.rb'
- 'spec/lib/microsoft_teams/notifier_spec.rb'
- 'spec/lib/object_storage/config_spec.rb'

View File

@ -43,7 +43,6 @@ Rails/IndexWith:
- 'spec/lib/gitlab/import_export/model_configuration_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/lib/google_api/cloud_platform/client_spec.rb'
- 'spec/lib/learn_gitlab/onboarding_spec.rb'
- 'spec/models/event_spec.rb'
- 'spec/presenters/projects/security/configuration_presenter_spec.rb'
- 'spec/support/database/multiple_databases.rb'

View File

@ -311,12 +311,10 @@ RSpec/ExpectInHook:
- 'spec/lib/gitlab/verify/uploads_spec.rb'
- 'spec/lib/gitlab/zentao/query_spec.rb'
- 'spec/lib/gitlab_spec.rb'
- 'spec/lib/learn_gitlab/onboarding_spec.rb'
- 'spec/lib/omni_auth/strategies/jwt_spec.rb'
- 'spec/lib/prometheus/pid_provider_spec.rb'
- 'spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb'
- 'spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb'
- 'spec/mailers/emails/service_desk_spec.rb'
- 'spec/metrics_server/metrics_server_spec.rb'
- 'spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb'

View File

@ -3,6 +3,5 @@ Style/FloatDivision:
Exclude:
- 'ee/app/models/geo_node_status.rb'
- 'ee/app/models/namespaces/storage/root_size.rb'
- 'lib/learn_gitlab/onboarding.rb'
- 'qa/qa/support/formatters/allure_metadata_formatter.rb'
- 'qa/qa/tools/reliable_report.rb'

View File

@ -20,7 +20,6 @@ Style/HashAsLastArrayItem:
- 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/types/boards/board_issuable_input_base_type.rb'
- 'app/graphql/types/boards/board_issue_input_base_type.rb'
- 'app/helpers/learn_gitlab_helper.rb'
- 'app/helpers/namespaces_helper.rb'
- 'app/models/customer_relations/contact.rb'
- 'app/models/customer_relations/organization.rb'

View File

@ -239,7 +239,6 @@ Style/SymbolProc:
- 'spec/graphql/mutations/releases/create_spec.rb'
- 'spec/graphql/types/work_items/widget_type_enum_spec.rb'
- 'spec/helpers/instance_configuration_helper_spec.rb'
- 'spec/helpers/learn_gitlab_helper_spec.rb'
- 'spec/helpers/members_helper_spec.rb'
- 'spec/lib/backup/gitaly_backup_spec.rb'
- 'spec/lib/gitlab/database/dynamic_model_helpers_spec.rb'

View File

@ -49,7 +49,7 @@ input[type='checkbox']:hover {
}
&.header-search-is-active {
.navbar-collapse {
.global-search-container {
flex-grow: 1;
}
@ -59,12 +59,6 @@ input[type='checkbox']:hover {
overflow: hidden;
}
}
@include media-breakpoint-up(xl) {
.navbar-nav {
padding-left: 1rem;
}
}
}
}

View File

@ -2088,6 +2088,12 @@ body.gl-dark {
.gl-pt-0 {
padding-top: 0;
}
.gl-mr-auto {
margin-right: auto;
}
.gl-mr-3 {
margin-right: 0.5rem;
}
.gl-ml-n2 {
margin-left: -0.25rem;
}

View File

@ -1739,6 +1739,12 @@ svg.s16 {
.gl-pt-0 {
padding-top: 0;
}
.gl-mr-auto {
margin-right: auto;
}
.gl-mr-3 {
margin-right: 0.5rem;
}
.gl-ml-n2 {
margin-left: -0.25rem;
}

View File

@ -22,7 +22,7 @@ module LearnGitlabHelper
def learn_gitlab_onboarding_available?(project)
Onboarding::Progress.onboarding?(project.namespace) &&
LearnGitlab::Project.new(current_user).available?
Onboarding::LearnGitlab.new(current_user).available?
end
private
@ -33,10 +33,12 @@ module LearnGitlabHelper
action_urls(project).to_h do |action, url|
[
action,
url: url,
completed: attributes[Onboarding::Progress.column_name(action)].present?,
svg: image_path("learn_gitlab/#{action}.svg"),
enabled: true
{
url: url,
completed: attributes[Onboarding::Progress.column_name(action)].present?,
svg: image_path("learn_gitlab/#{action}.svg"),
enabled: true
}
]
end
end
@ -70,11 +72,14 @@ module LearnGitlabHelper
end
def action_issue_urls
LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
Onboarding::Completion::ACTION_ISSUE_IDS.transform_values do |id|
project_issue_url(learn_gitlab_project, id)
end
end
def deploy_section_action_urls(project)
experiment(:security_actions_continuous_onboarding,
experiment(
:security_actions_continuous_onboarding,
namespace: project.namespace,
user: current_user,
sticky_to: current_user
@ -91,7 +96,7 @@ module LearnGitlabHelper
end
def learn_gitlab_project
@learn_gitlab_project ||= LearnGitlab::Project.new(current_user).project
@learn_gitlab_project ||= Onboarding::LearnGitlab.new(current_user).project
end
def onboarding_progress(project)

View File

@ -135,7 +135,7 @@ module Nav
id: 'general_new_group',
title: _('New group'),
href: new_group_path,
data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_group_link' }
)
)
end

View File

@ -281,52 +281,74 @@ module Nav
end
def projects_submenu_items(builder:)
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
[
{ id: 'your', title: _('Your projects'), href: dashboard_projects_path },
{ id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
{ id: 'explore', title: _('Explore projects'), href: explore_root_path },
{ id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
].each do |item|
if Feature.enabled?(:remove_extra_primary_submenu_options)
title = _('View all projects')
builder.add_primary_menu_item(
**item,
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
id: 'your',
title: title,
href: dashboard_projects_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
else
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
[
{ id: 'your', title: _('Your projects'), href: dashboard_projects_path },
{ id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
{ id: 'explore', title: _('Explore projects'), href: explore_root_path },
{ id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
].each do |item|
builder.add_primary_menu_item(
**item,
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
)
end
title = _('Create new project')
builder.add_secondary_menu_item(
id: 'create',
title: title,
href: new_project_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
end
title = _('Create new project')
builder.add_secondary_menu_item(
id: 'create',
title: title,
href: new_project_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
end
def groups_submenu
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
[
{ id: 'your', title: _('Your groups'), href: dashboard_groups_path },
{ id: 'explore', title: _('Explore groups'), href: explore_groups_path }
].each do |item|
if Feature.enabled?(:remove_extra_primary_submenu_options)
title = _('View all groups')
builder.add_primary_menu_item(
**item,
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
)
end
if current_user.can_create_group?
title = _('Create group')
builder.add_secondary_menu_item(
id: 'create',
id: 'your',
title: title,
href: new_group_path,
href: dashboard_groups_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
else
[
{ id: 'your', title: _('Your groups'), href: dashboard_groups_path },
{ id: 'explore', title: _('Explore groups'), href: explore_groups_path }
].each do |item|
builder.add_primary_menu_item(
**item,
data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
)
end
if current_user.can_create_group?
title = _('Create group')
builder.add_secondary_menu_item(
id: 'create',
title: title,
href: new_group_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
end
end
builder.build

View File

@ -13,33 +13,17 @@ module Ci
end
def within_freeze_period?(period)
# previous_freeze_end, ..., previous_freeze_start, ..., NOW, ..., next_freeze_end, ..., next_freeze_start
# Current time is within a freeze period if
# it falls between a previous freeze start and next freeze end
start_freeze = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
end_freeze = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
start_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
end_freeze_cron = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
previous_freeze_start = previous_time(start_freeze)
previous_freeze_end = previous_time(end_freeze)
next_freeze_start = next_time(start_freeze)
next_freeze_end = next_time(end_freeze)
start_freeze = start_freeze_cron.previous_time_from(time_zone_now)
end_freeze = end_freeze_cron.next_time_from(start_freeze)
previous_freeze_end < previous_freeze_start &&
previous_freeze_start <= time_zone_now &&
time_zone_now <= next_freeze_end &&
next_freeze_end < next_freeze_start
start_freeze <= time_zone_now && time_zone_now <= end_freeze
end
private
def previous_time(cron_parser)
cron_parser.previous_time_from(time_zone_now)
end
def next_time(cron_parser)
cron_parser.next_time_from(time_zone_now)
end
def time_zone_now
@time_zone_now ||= Time.zone.now
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module LearnGitlab
class Onboarding
module Onboarding
class Completion
include Gitlab::Utils::StrongMemoize
include Gitlab::Experiment::Dsl
@ -24,7 +24,7 @@ module LearnGitlab
@current_user = current_user
end
def completed_percentage
def percentage
return 0 unless onboarding_progress
attributes = onboarding_progress.attributes.symbolize_keys
@ -32,14 +32,14 @@ module LearnGitlab
total_actions = action_columns.count
completed_actions = action_columns.count { |column| attributes[column].present? }
(completed_actions.to_f / total_actions.to_f * 100).round
(completed_actions.to_f / total_actions * 100).round
end
private
def onboarding_progress
strong_memoize(:onboarding_progress) do
::Onboarding::Progress.find_by(namespace: namespace) # rubocop: disable CodeReuse/ActiveRecord
::Onboarding::Progress.find_by(namespace: namespace)
end
end
@ -54,7 +54,8 @@ module LearnGitlab
end
def deploy_section_tracked_actions
experiment(:security_actions_continuous_onboarding,
experiment(
:security_actions_continuous_onboarding,
namespace: namespace,
user: current_user,
sticky_to: current_user

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module LearnGitlab
class Project
module Onboarding
class LearnGitlab
PROJECT_NAME = 'Learn GitLab'
PROJECT_NAME_ULTIMATE_TRIAL = 'Learn GitLab - Ultimate trial'
BOARD_NAME = 'GitLab onboarding'

View File

@ -5,7 +5,7 @@
%a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content
.container-fluid
.header-content.js-header-content
.title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0
.title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0.gl-mr-3
.title
%span.gl-sr-only GitLab
= link_to root_path, title: _('Dashboard'), id: 'logo', class: 'has-tooltip', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
@ -28,11 +28,25 @@
.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
.navbar-collapse.gl-transition-medium.collapse
- if top_nav_show_search && Feature.enabled?(:new_navbar_layout)
.navbar-collapse.gl-transition-medium.collapse.gl-mr-auto.global-search-container.hide-when-top-nav-responsive-open
- search_menu_item = top_nav_search_menu_item_attrs
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center
%li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full
- unless current_controller?(:search)
- if Feature.enabled?(:new_header_search)
= render 'layouts/header_search'
- else
= render 'layouts/search'
%li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' }
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon(search_menu_item.fetch(:icon))
.navbar-collapse.gl-transition-medium.collapse{ class: ('global-search-container' unless Feature.enabled?(:new_navbar_layout)) }
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end
- if current_user
= render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right'
- if top_nav_show_search
- if top_nav_show_search && Feature.disabled?(:new_navbar_layout)
- search_menu_item = top_nav_search_menu_item_attrs
%li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full
- unless current_controller?(:search)

View File

@ -1,8 +1,8 @@
---
name: new_navbar_layout
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96853
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366082
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
milestone: '15.4'
type: development
group: group::foundations
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: remove_extra_primary_submenu_options
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96931
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
milestone: '15.4'
type: development
group: group::foundations
default_enabled: true

View File

@ -0,0 +1,27 @@
---
key_path: redis_hll_counters.quickactions.i_quickactions_timeline_monthly
name: quickactions_timeline_monthly
description: Count of MAU using the `/timeline` quick action
product_section: ops
product_stage: monitor
product_group: respond
product_category: incident_management
value_type: number
status: active
milestone: "15.4"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97020
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_timeline
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -0,0 +1,27 @@
---
key_path: redis_hll_counters.quickactions.i_quickactions_timeline_weekly
name: quickactions_timeline_weekly
description: Count of WAU using the `/timeline` quick action
product_section: ops
product_stage: monitor
product_group: respond
product_category: incident_management
value_type: number
status: active
milestone: "15.4"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97020
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_timeline
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddDismissalReasonToVulnerabilityStateTransitions < Gitlab::Database::Migration[2.0]
def change
add_column :vulnerability_state_transitions, :dismissal_reason, :smallint
end
end

View File

@ -0,0 +1 @@
34e485c0c94960fc07a3f529aed749c2bbc1a72bb49d064225a37b85134f70f2

View File

@ -22823,6 +22823,7 @@ CREATE TABLE vulnerability_state_transitions (
updated_at timestamp with time zone NOT NULL,
author_id bigint,
comment text,
dismissal_reason smallint,
CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255))
);

View File

@ -52,6 +52,12 @@ To create a timeline event:
1. Complete the required fields.
1. Select **Save** or **Save and add another event**.
### Using a quick action
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4.
You can create a timeline event using the `/timeline` [quick action](../../user/project/quick_actions.md).
### From a comment on the incident
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344058) in GitLab 15.4.

View File

@ -17,8 +17,8 @@ and apply those changes to another branch. Cherry-picks can help you:
You can cherry-pick commits from the command line. In the GitLab user interface,
you can also:
- Cherry-pick [all changes from a merge request](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-merge-request).
- Cherry-pick [a single commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit).
- Cherry-pick [all changes from a merge request](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-all-changes-from-a-merge-request).
- Cherry-pick [a single commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit).
- Cherry-pick [from a fork to the upstream repository](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-into-a-project).
## Cherry-pick from the command line

View File

@ -36,7 +36,7 @@ The following resources can help you get started with Git:
- [GitLab Git Cheat Sheet (download)](https://about.gitlab.com/images/press/git-cheat-sheet.pdf)
- Commits:
- [Revert a commit](../../user/project/merge_requests/revert_changes.md#revert-a-commit)
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md)
- [Squashing commits](../gitlab_flow.md#squashing-commits-with-rebase)
- [Squash-and-merge](../../user/project/merge_requests/squash_and_merge.md)
- [Signing commits](../../user/project/repository/gpg_signed_commits/index.md)

View File

@ -209,7 +209,7 @@ To recover from multiple incorrect commits:
The commits are now `A-B-C-D-E`.
Alternatively, with GitLab,
you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit)
that commit into a new merge request.
NOTE:

View File

@ -4,7 +4,7 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Migrate from GitLab Managed Apps to Cluster Management Projects **(FREE)**
# Migrate from GitLab Managed Apps to Cluster Management Projects (DEPRECATED) **(FREE)**
The GitLab Managed Apps were deprecated in GitLab 14.0
in favor of user-controlled Cluster Management projects.

View File

@ -7,61 +7,79 @@ type: reference, concepts
# Cherry-pick changes **(FREE)**
GitLab implements Git's powerful feature to
[cherry-pick any commit](https://git-scm.com/docs/git-cherry-pick "Git cherry-pick documentation")
with a **Cherry-pick** button in merge requests and commit details.
In Git, *cherry-picking* is taking a single commit from one branch and adding it
as the latest commit on another branch. The rest of the commits in the source branch
are not added to the target. You should cherry-pick a commit when you need the
change contained in a single commit, but you can't or don't want to pull the
entire contents of that branch into another.
## Cherry-pick a merge request
After the merge request has been merged, a **Cherry-pick** button displays
to cherry-pick the changes introduced by that merge request.
![Cherry-pick merge request](img/cherry_pick_changes_mr.png)
After you select that button, a modal displays a
[branch filter search box](../repository/branches/index.md#branch-filter-search-box)
where you can choose to either:
- Cherry-pick the changes directly into the selected branch.
- Create a new merge request with the cherry-picked changes.
### Track a cherry-pick
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2675) in GitLab 12.9.
When you cherry-pick a merge commit, GitLab displays a system note to the related merge
request thread. It crosslinks the new commit and the existing merge request.
![Cherry-pick tracking in merge request timeline](img/cherry_pick_mr_timeline_v12_9.png)
Each deployment's [list of associated merge requests](../../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment) includes cherry-picked merge commits.
You can use the GitLab UI to cherry-pick single commits or entire merge requests.
You can even cherry-pick a commit from [a fork of your project](#cherry-pick-into-a-project).
NOTE:
We only track cherry-pick executed from GitLab (both UI and API). Support for tracking cherry-picked commits through the command line
Support for tracking commits cherry-picked from the command line
is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/202215).
## Cherry-pick a commit
## Cherry-pick all changes from a merge request
You can cherry-pick a commit from the commit details page:
After a merge request is merged, you can cherry-pick all changes introduced
by the merge request:
![Cherry-pick commit](img/cherry_pick_changes_commit.png)
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests**, and find your merge request.
1. Scroll to the merge request reports section, and find the **Merged by** report.
1. In the top right, select **Cherry-pick**:
Similar to cherry-picking a merge request, you can cherry-pick the changes
directly into the target branch or create a new merge request to cherry-pick the
changes.
![Cherry-pick merge request](img/cherry_pick_v15_4.png)
1. In the modal window, select the project and branch to cherry-pick into.
1. Optional. Select **Start a new merge request with these changes**.
1. Select **Cherry-pick**.
When cherry-picking merge commits, the mainline is always the
first parent. If you want to use a different mainline, you need to do that
from the command line.
## Cherry-pick a single commit
Here's a quick example to cherry-pick a merge commit using the second parent as the
mainline:
You can cherry-pick a single commit from multiple locations in your GitLab project.
```shell
git cherry-pick -m 2 7a39eb0
```
### From a project's commit list
### Cherry-pick into a project
To cherry-pick a commit from the list of all commits for a project:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Commits**.
1. Select the title of the commit you want to cherry-pick.
1. In the modal window, select the project and branch to cherry-pick into.
1. Optional. Select **Start a new merge request with these changes**.
1. Select **Cherry-pick**.
### From a merge request
You can cherry-pick commits from any merge request in your project, regardless of
whether the merge request is open or closed. To cherry-pick a commit from the
list of commits included in a merge request:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests**, and find your merge request.
1. In the merge request's secondary menu, select **Commits** to display the commit details page.
1. Select the title of the commit you want to cherry-pick.
1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
1. In the modal window, select the project and branch to cherry-pick into.
1. Optional. Select **Start a new merge request with these changes**.
1. Select **Cherry-pick**.
### From the file view of a repository
You can cherry-pick from the list of previous commits affecting an individual file
when you view that file in your project's Git repository:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Files** and go to the file
changed by the commit.
1. Select **History**, then select the title of the commit you want to cherry-pick.
1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
1. In the modal window, select the project and branch to cherry-pick into.
1. Optional. Select **Start a new merge request with these changes**.
1. Select **Cherry-pick**.
## Cherry-pick into a project
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21268) in GitLab 13.11 behind a [feature flag](../../feature_flags.md), disabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/324154) in GitLab 14.0.
@ -70,25 +88,38 @@ You can cherry-pick merge requests from the same project, or forks of the same
project, from the GitLab user interface:
1. In the merge request's secondary menu, select **Commits** to display the commit details page.
1. Select the **Options** dropdown and select **Cherry-pick** to show the cherry-pick modal.
1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
1. In **Pick into project** and **Pick into branch**, select the destination project and branch:
![Cherry-pick commit](img/cherry_pick_into_project_v13_11.png)
1. Optional. Select **Start a new merge request** if you're ready to create a merge request.
1. Select **Cherry-pick**.
## View system notes for cherry-picked commits
When you cherry-pick a merge commit in the GitLab UI or API, GitLab adds a system note
to the related merge request thread in the format **{cherry-pick-commit}**
`[USER]` **picked the changes into the branch** `[BRANCHNAME]` with commit** `[SHA]` `[DATE]`:
![Cherry-pick tracking in merge request timeline](img/cherry_pick_mr_timeline_v15_4.png)
The system note crosslinks the new commit and the existing merge request.
Each deployment's [list of associated merge requests](../../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment) includes cherry-picked merge commits.
## Related topics
- The [Commits API](../../../api/commits.md) enables you to add custom messages
to changes you cherry-pick through the API.
- Use the [Commits API](../../../api/commits.md) to add custom messages
to changes when you use the API to cherry-pick.
<!-- ## Troubleshooting
## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
### Selecting a different parent commit when cherry-picking
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
When you cherry-pick a merge commit in the GitLab UI, the mainline is always the
first parent. Use the command line to cherry-pick with a different mainline.
Here's a quick example to cherry-pick a merge commit using the second parent as the
mainline:
```shell
git cherry-pick -m 2 7a39eb0
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -110,6 +110,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
| `/timeline <timeline comment> \| <date(YYYY-MM-DD)> <time(HH:MM)>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a timeline event to this incident. For example, `/timeline DB load spiked \| 2022-09-07 09:30`. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4). |
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. |
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) in GitLab 14.3 |
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees. |

View File

@ -49,7 +49,7 @@ to a branch in the repository. When you use the command line, you can commit mul
on their respective thread.
- **Cherry-pick a commit:**
In GitLab, you can
[cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
[cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit)
from the UI.
- **Revert a commit:**
[Revert a commit](../merge_requests/revert_changes.md#revert-a-commit)

View File

@ -317,6 +317,30 @@ module Gitlab
command :remove_contacts do |contact_emails|
@updates[:remove_contacts] = contact_emails.split(' ')
end
desc { _('Add a timeline event to incident') }
explanation { _('Adds a timeline event to incident.') }
params '<timeline comment> | <date(YYYY-MM-DD)> <time(HH:MM)>'
types Issue
condition do
quick_action_target.incident? &&
current_user.can?(:admin_incident_management_timeline_event, quick_action_target)
end
parse_params do |event_params|
Gitlab::QuickActions::TimelineTextAndDateTimeSeparator.new(event_params).execute
end
command :timeline do |event_text, date_time|
if event_text && date_time
timeline_event = timeline_event_create_service(event_text, date_time).execute
@execution_message[:timeline] =
if timeline_event.success?
_('Timeline event added successfully.')
else
_('Something went wrong while adding timeline event.')
end
end
end
end
private
@ -336,6 +360,10 @@ module Gitlab
def merge_updates(result, update_hash)
update_hash.merge!(result.payload) if result.payload
end
def timeline_event_create_service(event_text, event_date_time)
::IncidentManagement::TimelineEvents::CreateService.new(quick_action_target, current_user, { note: event_text, occurred_at: event_date_time, editable: true })
end
end
end
end

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
module Gitlab
module QuickActions
class TimelineTextAndDateTimeSeparator
DATETIME_REGEX = %r{(\d{2,4}[\-.]\d{1,2}[\-.]\d{1,2} \d{1,2}:\d{2})}.freeze
MIXED_DELIMITER = %r{([/.])}.freeze
TIME_REGEX = %r{(\d{1,2}:\d{2})}.freeze
def initialize(timeline_event_arg)
@timeline_event_arg = timeline_event_arg
@timeline_text = get_text
@timeline_date_string = get_raw_date_string
end
def execute
return if @timeline_event_arg.blank?
return if date_contains_mixed_delimiters?
return [@timeline_text, get_current_date_time] unless date_time_present?
return unless valid_date?
[@timeline_text, get_actual_date_time]
end
private
def get_text
@timeline_event_arg.split('|')[0]&.strip
end
def get_raw_date_string
@timeline_event_arg.split('|')[1]&.strip
end
def get_current_date_time
DateTime.current.strftime("%Y-%m-%d %H:%M:00 UTC")
end
def get_actual_date_time
DateTime.parse(@timeline_date_string)
end
def date_time_present?
DATETIME_REGEX =~ @timeline_date_string || TIME_REGEX =~ @timeline_date_string
end
def date_contains_mixed_delimiters?
MIXED_DELIMITER =~ @timeline_date_string
end
def valid_date?
get_actual_date_time
rescue Date::Error
nil
end
end
end
end

View File

@ -127,6 +127,10 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_timeline
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_page
category: quickactions
redis_slot: quickactions

View File

@ -29,10 +29,10 @@ module Sidebars
override :pill_count
def pill_count
strong_memoize(:pill_count) do
percentage = LearnGitlab::Onboarding.new(
percentage = Onboarding::Completion.new(
context.project.namespace,
context.current_user
).completed_percentage
).percentage
"#{percentage}%"
end

View File

@ -2201,6 +2201,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
msgid "Add a timeline event to incident"
msgstr ""
msgid "Add a title..."
msgstr ""
@ -2471,6 +2474,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
msgid "Adds a timeline event to incident."
msgstr ""
msgid "Adds a to do."
msgstr ""
@ -37111,6 +37117,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
msgid "Something went wrong while adding timeline event."
msgstr ""
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@ -40971,6 +40980,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
msgid "Timeline event added successfully."
msgstr ""
msgid "Timeline|Turn recent updates view off"
msgstr ""
@ -43386,9 +43398,15 @@ msgstr ""
msgid "View all environments."
msgstr ""
msgid "View all groups"
msgstr ""
msgid "View all issues"
msgstr ""
msgid "View all projects"
msgstr ""
msgid "View blame"
msgstr ""

View File

@ -53,28 +53,45 @@ module QA
element :search_term_field
end
view 'app/views/layouts/header/_new_dropdown.html.haml' do
element :new_menu_toggle
end
view 'app/helpers/nav/new_dropdown_helper.rb' do
element :global_new_group_link
element :global_new_project_link
end
def go_to_groups
within_groups_menu do
click_element(:menu_item_link, title: 'Your groups')
# Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
if has_element?(:menu_item_link, title: 'Your groups')
click_element(:menu_item_link, title: 'Your groups')
else
click_element(:menu_item_link, title: 'View all groups')
end
end
end
def go_to_create_group
within_groups_menu do
click_element(:menu_item_link, title: 'Create group')
end
click_element(:new_menu_toggle)
click_element(:global_new_group_link)
end
def go_to_projects
within_projects_menu do
click_element(:menu_item_link, title: 'Your projects')
# Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
if has_element?(:menu_item_link, title: 'Your projects')
click_element(:menu_item_link, title: 'Your projects')
else
click_element(:menu_item_link, title: 'View all projects')
end
end
end
def go_to_create_project
within_projects_menu do
click_element(:menu_item_link, title: 'Create new project')
end
click_element(:new_menu_toggle)
click_element(:global_new_project_link)
end
def go_to_menu_dropdown_option(option_name)

View File

@ -33,7 +33,7 @@ RSpec.describe 'Admin mode', :js do
open_top_nav_projects
within_top_nav do
click_link('Your projects')
click_link('View all projects')
end
expect(page).to have_current_path(dashboard_projects_path)
@ -99,7 +99,7 @@ RSpec.describe 'Admin mode', :js do
open_top_nav_projects
within_top_nav do
click_link('Your projects')
click_link('View all projects')
end
expect(page).to have_current_path(dashboard_projects_path)

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Incidents > User uses quick actions', :js do
include Spec::Support::Helpers::Features::NotesHelpers
describe 'incident-only commands' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:issue, reload: true) { create(:incident, project: project) }
before do
project.add_developer(user)
sign_in(user)
visit project_issue_path(project, issue)
wait_for_all_requests
end
after do
wait_for_requests
end
it_behaves_like 'timeline quick action'
end
end

View File

@ -339,7 +339,7 @@ RSpec.describe 'User page' do
subject
page.within '.navbar-nav' do
page.within '.navbar-gitlab' do
expect(page).to have_link('Sign in')
end
end
@ -351,7 +351,7 @@ RSpec.describe 'User page' do
subject
page.within '.navbar-nav' do
page.within '.navbar-gitlab' do
expect(page).to have_link('Sign in / Register')
end
end

View File

@ -7,11 +7,11 @@ RSpec.describe LearnGitlabHelper do
include Devise::Test::ControllerHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME, namespace: user.namespace) }
let_it_be(:project) { create(:project, name: Onboarding::LearnGitlab::PROJECT_NAME, namespace: user.namespace) }
let_it_be(:namespace) { project.namespace }
before do
allow_next_instance_of(LearnGitlab::Project) do |learn_gitlab|
allow_next_instance_of(Onboarding::LearnGitlab) do |learn_gitlab|
allow(learn_gitlab).to receive(:project).and_return(project)
end
@ -38,7 +38,7 @@ RSpec.describe LearnGitlabHelper do
with_them do
before do
allow(Onboarding::Progress).to receive(:onboarding?).with(project.namespace).and_return(onboarding)
allow_next(LearnGitlab::Project, user).to receive(:available?).and_return(learn_gitlab_available)
allow_next(Onboarding::LearnGitlab, user).to receive(:available?).and_return(learn_gitlab_available)
end
context 'when signed in' do
@ -81,7 +81,7 @@ RSpec.describe LearnGitlabHelper do
it 'has all section data', :aggregate_failures do
expect(onboarding_sections_data.keys).to contain_exactly(:deploy, :plan, :workspace)
expect(onboarding_sections_data.values.map { |section| section.keys }).to match_array([[:svg]] * 3)
expect(onboarding_sections_data.values.map(&:keys)).to match_array([[:svg]] * 3)
end
it 'has all project data', :aggregate_failures do

View File

@ -100,7 +100,7 @@ RSpec.describe Nav::NewDropdownHelper do
id: 'general_new_group',
title: 'New group',
href: '/groups/new',
data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
data: { qa_selector: 'global_new_group_link', track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
)
)
)

View File

@ -161,61 +161,87 @@ RSpec.describe Nav::TopNavHelper do
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Your projects',
**menu_data_tracking_attrs('your_projects')
qa_title: 'View all projects',
**menu_data_tracking_attrs('view_all_projects')
},
href: '/dashboard/projects',
id: 'your',
title: 'Your projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Starred projects',
**menu_data_tracking_attrs('starred_projects')
},
href: '/dashboard/projects/starred',
id: 'starred',
title: 'Starred projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore projects',
**menu_data_tracking_attrs('explore_projects')
},
href: '/explore',
id: 'explore',
title: 'Explore projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore topics',
**menu_data_tracking_attrs('explore_topics')
},
href: '/explore/projects/topics',
id: 'topics',
title: 'Explore topics'
title: 'View all projects'
)
]
expect(projects_view[:linksPrimary]).to eq(expected_links_primary)
end
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Create new project',
**menu_data_tracking_attrs('create_new_project')
},
href: '/projects/new',
id: 'create',
title: 'Create new project'
)
]
expect(projects_view[:linksSecondary]).to eq(expected_links_secondary)
it 'does not have any :linksSecondary' do
expect(projects_view[:linksSecondary]).to eq([])
end
context 'when extra submenu options are not hidden' do
before do
stub_feature_flags(remove_extra_primary_submenu_options: false)
end
it 'has expected :linksPrimary' do
expected_links_primary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Your projects',
**menu_data_tracking_attrs('your_projects')
},
href: '/dashboard/projects',
id: 'your',
title: 'Your projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Starred projects',
**menu_data_tracking_attrs('starred_projects')
},
href: '/dashboard/projects/starred',
id: 'starred',
title: 'Starred projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore projects',
**menu_data_tracking_attrs('explore_projects')
},
href: '/explore',
id: 'explore',
title: 'Explore projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore topics',
**menu_data_tracking_attrs('explore_topics')
},
href: '/explore/projects/topics',
id: 'topics',
title: 'Explore topics'
)
]
expect(projects_view[:linksPrimary]).to eq(expected_links_primary)
end
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Create new project',
**menu_data_tracking_attrs('create_new_project')
},
href: '/projects/new',
id: 'create',
title: 'Create new project'
)
]
expect(projects_view[:linksSecondary]).to eq(expected_links_secondary)
end
end
context 'with current nav as project' do
@ -300,41 +326,67 @@ RSpec.describe Nav::TopNavHelper do
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Your groups',
**menu_data_tracking_attrs('your_groups')
qa_title: 'View all groups',
**menu_data_tracking_attrs('view_all_groups')
},
href: '/dashboard/groups',
id: 'your',
title: 'Your groups'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore groups',
**menu_data_tracking_attrs('explore_groups')
},
href: '/explore/groups',
id: 'explore',
title: 'Explore groups'
title: 'View all groups'
)
]
expect(groups_view[:linksPrimary]).to eq(expected_links_primary)
end
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Create group',
**menu_data_tracking_attrs('create_group')
},
href: '/groups/new',
id: 'create',
title: 'Create group'
)
]
expect(groups_view[:linksSecondary]).to eq(expected_links_secondary)
it 'does not have any :linksSecondary' do
expect(groups_view[:linksSecondary]).to eq([])
end
context 'when extra submenu options are not hidden' do
before do
stub_feature_flags(remove_extra_primary_submenu_options: false)
end
it 'has expected :linksPrimary' do
expected_links_primary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Your groups',
**menu_data_tracking_attrs('your_groups')
},
href: '/dashboard/groups',
id: 'your',
title: 'Your groups'
),
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Explore groups',
**menu_data_tracking_attrs('explore_groups')
},
href: '/explore/groups',
id: 'explore',
title: 'Explore groups'
)
]
expect(groups_view[:linksPrimary]).to eq(expected_links_primary)
end
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
qa_title: 'Create group',
**menu_data_tracking_attrs('create_group')
},
href: '/groups/new',
id: 'create',
title: 'Create group'
)
]
expect(groups_view[:linksSecondary]).to eq(expected_links_secondary)
end
end
context 'with external user' do

View File

@ -0,0 +1,94 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::QuickActions::TimelineTextAndDateTimeSeparator do
subject(:timeline_text_and_datetime_separator) { described_class }
shared_examples 'arg line with invalid parameters' do
it 'returns nil' do
expect(timeline_text_and_datetime_separator.new(invalid_arg).execute).to eq(nil)
end
end
shared_examples 'arg line with valid parameters' do
it 'returns text and date time array' do
freeze_time do
expect(timeline_text_and_datetime_separator.new(valid_arg).execute).to eq(expected_response)
end
end
end
describe 'execute' do
context 'with invalid parameters in arg line' do
context 'with empty arg line' do
it_behaves_like 'arg line with invalid parameters' do
let(:invalid_arg) { '' }
end
end
context 'with invalid date' do
it_behaves_like 'arg line with invalid parameters' do
let(:invalid_arg) { 'timeline comment | 2022-13-13 09:30' }
end
it_behaves_like 'arg line with invalid parameters' do
let(:invalid_arg) { 'timeline comment | 2022-09/09 09:30' }
end
it_behaves_like 'arg line with invalid parameters' do
let(:invalid_arg) { 'timeline comment | 2022-09.09 09:30' }
end
end
context 'with invalid time' do
it_behaves_like 'arg line with invalid parameters' do
let(:invalid_arg) { 'timeline comment | 2022-11-13 29:30' }
end
end
context 'when date is invalid in arg line' do
let(:invalid_arg) { 'timeline comment | wrong data type' }
it 'return current date' do
timeline_args = timeline_text_and_datetime_separator.new(invalid_arg).execute
expect(timeline_args).to be_an_instance_of(Array)
expect(timeline_args.first).to eq('timeline comment')
expect(timeline_args.second).to match(Gitlab::QuickActions::TimelineTextAndDateTimeSeparator::DATETIME_REGEX)
end
end
end
context 'with valid parameters' do
context 'when only timeline text present in arg line' do
it_behaves_like 'arg line with valid parameters' do
let(:timeline_text) { 'timeline comment' }
let(:valid_arg) { timeline_text }
let(:date) { DateTime.current.strftime("%Y-%m-%d %H:%M:00 UTC") }
let(:expected_response) { [timeline_text, date] }
end
end
context 'when only timeline text and time present in arg line' do
it_behaves_like 'arg line with valid parameters' do
let(:timeline_text) { 'timeline comment' }
let(:date) { '09:30' }
let(:valid_arg) { "#{timeline_text} | #{date}" }
let(:parsed_date) { DateTime.parse(date) }
let(:expected_response) { [timeline_text, parsed_date] }
end
end
context 'when timeline text and date is present in arg line' do
it_behaves_like 'arg line with valid parameters' do
let(:timeline_text) { 'timeline comment' }
let(:date) { '2022-06-05 09:30' }
let(:valid_arg) { "#{timeline_text} | #{date}" }
let(:parsed_date) { DateTime.parse(date) }
let(:expected_response) { [timeline_text, parsed_date] }
end
end
end
end
end

View File

@ -68,13 +68,11 @@ RSpec.describe Sidebars::Projects::Menus::LearnGitlabMenu do
end
describe '#pill_count' do
before do
expect_next_instance_of(LearnGitlab::Onboarding) do |onboarding|
expect(onboarding).to receive(:completed_percentage).and_return(20)
end
end
it 'returns pill count' do
expect_next_instance_of(Onboarding::Completion) do |onboarding|
expect(onboarding).to receive(:percentage).and_return(20)
end
expect(subject.pill_count).to eq '20%'
end
end

View File

@ -59,4 +59,13 @@ RSpec.describe Ci::FreezePeriodStatus do
it_behaves_like 'outside freeze period', Time.utc(2020, 4, 13, 8, 1)
end
# https://gitlab.com/gitlab-org/gitlab/-/issues/370472
context 'when period overlaps with itself' do
let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '* * * 8 *', freeze_end: '* * * 10 *') }
it_behaves_like 'within freeze period', Time.utc(2020, 8, 11, 0, 0)
it_behaves_like 'outside freeze period', Time.utc(2020, 10, 11, 0, 0)
end
end

View File

@ -2,13 +2,11 @@
require 'spec_helper'
RSpec.describe LearnGitlab::Onboarding do
describe '#completed_percentage' do
RSpec.describe Onboarding::Completion do
describe '#percentage' do
let(:completed_actions) { {} }
let(:onboarding_progress) { build(:onboarding_progress, namespace: namespace, **completed_actions) }
let(:namespace) { create(:namespace) }
let_it_be(:tracked_action_columns) do
let!(:onboarding_progress) { create(:onboarding_progress, namespace: namespace, **completed_actions) }
let(:tracked_action_columns) do
[
*described_class::ACTION_ISSUE_IDS.keys,
*described_class::ACTION_PATHS,
@ -16,14 +14,12 @@ RSpec.describe LearnGitlab::Onboarding do
].map { |key| ::Onboarding::Progress.column_name(key) }
end
before do
expect(::Onboarding::Progress).to receive(:find_by).with(namespace: namespace).and_return(onboarding_progress)
end
let_it_be(:namespace) { create(:namespace) }
subject { described_class.new(namespace).completed_percentage }
subject { described_class.new(namespace).percentage }
context 'when no onboarding_progress exists' do
let(:onboarding_progress) { nil }
subject { described_class.new(build(:namespace)).percentage }
it { is_expected.to eq(0) }
end
@ -34,13 +30,13 @@ RSpec.describe LearnGitlab::Onboarding do
context 'when all tracked actions have been completed' do
let(:completed_actions) do
tracked_action_columns.to_h { |action| [action, Time.current] }
tracked_action_columns.index_with { Time.current }
end
it { is_expected.to eq(100) }
end
describe 'security_actions_continuous_onboarding experiment' do
context 'with security_actions_continuous_onboarding experiment' do
let(:completed_actions) { Hash[tracked_action_columns.first, Time.current] }
context 'when control' do

View File

@ -2,17 +2,17 @@
require 'spec_helper'
RSpec.describe LearnGitlab::Project do
RSpec.describe Onboarding::LearnGitlab do
let_it_be(:current_user) { create(:user) }
let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME) }
let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: LearnGitlab::Project::BOARD_NAME) }
let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: LearnGitlab::Project::LABEL_NAME) }
let_it_be(:learn_gitlab_project) { create(:project, name: described_class::PROJECT_NAME) }
let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: described_class::BOARD_NAME) }
let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: described_class::LABEL_NAME) }
before do
learn_gitlab_project.add_developer(current_user)
end
describe '.available?' do
describe '#available?' do
using RSpec::Parameterized::TableSyntax
where(:project, :board, :label, :expected_result) do
@ -41,25 +41,27 @@ RSpec.describe LearnGitlab::Project do
end
end
describe '.project' do
describe '#project' do
subject { described_class.new(current_user).project }
it { is_expected.to eq learn_gitlab_project }
context 'when it is created during trial signup' do
let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME_ULTIMATE_TRIAL, path: 'learn-gitlab-ultimate-trial') }
let_it_be(:learn_gitlab_project) do
create(:project, name: described_class::PROJECT_NAME_ULTIMATE_TRIAL, path: 'learn-gitlab-ultimate-trial')
end
it { is_expected.to eq learn_gitlab_project }
end
end
describe '.board' do
describe '#board' do
subject { described_class.new(current_user).board }
it { is_expected.to eq learn_gitlab_board }
end
describe '.label' do
describe '#label' do
subject { described_class.new(current_user).label }
it { is_expected.to eq learn_gitlab_label }

View File

@ -537,7 +537,6 @@
- './ee/spec/features/trials/capture_lead_spec.rb'
- './ee/spec/features/trials/select_namespace_spec.rb'
- './ee/spec/features/trials/show_trial_banner_spec.rb'
- './ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- './ee/spec/features/users/arkose_labs_csp_spec.rb'
- './ee/spec/features/users/login_spec.rb'
- './ee/spec/features/users/signup_spec.rb'
@ -5459,7 +5458,6 @@
- './spec/helpers/keyset_helper_spec.rb'
- './spec/helpers/labels_helper_spec.rb'
- './spec/helpers/lazy_image_tag_helper_spec.rb'
- './spec/helpers/learn_gitlab_helper_spec.rb'
- './spec/helpers/listbox_helper_spec.rb'
- './spec/helpers/markup_helper_spec.rb'
- './spec/helpers/members_helper_spec.rb'
@ -7756,8 +7754,6 @@
- './spec/lib/json_web_token/token_spec.rb'
- './spec/lib/kramdown/kramdown_spec.rb'
- './spec/lib/kramdown/parser/atlassian_document_format_spec.rb'
- './spec/lib/learn_gitlab/onboarding_spec.rb'
- './spec/lib/learn_gitlab/project_spec.rb'
- './spec/lib/marginalia_spec.rb'
- './spec/lib/mattermost/client_spec.rb'
- './spec/lib/mattermost/command_spec.rb'
@ -7824,7 +7820,6 @@
- './spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/issues_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/monitor_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb'

View File

@ -0,0 +1,82 @@
# frozen_string_literal: true
RSpec.shared_examples 'timeline quick action' do
describe '/timeline' do
context 'with valid args' do
where(:timeline_text, :date_time_arg) do
[
['timeline comment', '2022-09-09 09:30'],
['new timeline comment', '09:30'],
['another timeline comment', ' 2022-09-09 09:15']
]
end
with_them do
it 'adds a timeline event' do
add_note("/timeline #{timeline_text} | #{date_time_arg}")
expect(page).to have_content('Timeline event added successfully.')
expect(issue.incident_management_timeline_events.first.note).to eq(timeline_text)
expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime.parse(date_time_arg))
end
end
it 'adds a timeline event when no date is passed' do
freeze_time do
add_note('/timeline timeline event with not date')
expect(page).to have_content('Timeline event added successfully.')
expect(issue.incident_management_timeline_events.first.note).to eq('timeline event with not date')
expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime
.current.strftime("%Y-%m-%d %H:%M:00 UTC"))
end
end
it 'adds a timeline event when only date is passed' do
freeze_time do
add_note('/timeline timeline event with not date | 2022-10-11')
expect(page).to have_content('Timeline event added successfully.')
expect(issue.incident_management_timeline_events.first.note).to eq('timeline event with not date')
expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime
.current.strftime("%Y-%m-%d %H:%M:00 UTC"))
end
end
end
context 'with invalid args' do
where(:timeline_text, :date_time_arg) do
[
['timeline comment', '2022-13-13 09:30'],
['timeline comment 2', '2022-09-06 24:30']
]
end
with_them do
it 'does not add a timeline event' do
add_note("/timeline #{timeline_text} | #{date_time_arg}")
expect(page).to have_content('Failed to apply commands.')
expect(issue.incident_management_timeline_events.length).to eq(0)
end
end
end
context 'when create service fails' do
before do
allow_next_instance_of(::IncidentManagement::TimelineEvents::CreateService) do |service|
allow(service).to receive(:execute).and_return(
ServiceResponse.error(payload: { timeline_event: nil }, message: 'Some error')
)
end
end
it 'does not add a timeline event' do
add_note('/timeline text | 2022-09-10 09:30')
expect(page).to have_content('Something went wrong while adding timeline event.')
expect(issue.incident_management_timeline_events.length).to eq(0)
end
end
end
end

View File

@ -70,8 +70,8 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
describe 'Learn GitLab' do
it 'has a link to the learn GitLab' do
allow(view).to receive(:learn_gitlab_enabled?).and_return(true)
allow_next_instance_of(LearnGitlab::Onboarding) do |onboarding|
expect(onboarding).to receive(:completed_percentage).and_return(20)
allow_next_instance_of(Onboarding::Completion) do |onboarding|
expect(onboarding).to receive(:percentage).and_return(20)
end
render