Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-10-12 03:08:54 +00:00
parent a4dd029f24
commit 129d7ea3db
39 changed files with 500 additions and 153 deletions

View File

@ -160,9 +160,7 @@ gdk-qa-reliable:
QA_RUN_TYPE: gdk-qa-blocking
parallel: 10
rules:
- if: $CI_MERGE_REQUEST_LABELS =~ /devops::govern|devops::create|devops::verify|devops::manage|devops::data stores/
- when: on_success
allow_failure: true
- when: always
gdk-qa-reliable-with-load-balancer:
extends:

View File

@ -125,7 +125,6 @@ export default {
<template>
<li
data-qa-selector="board_card"
:class="[
{
'multi-select gl-bg-blue-50 gl-border-blue-200': multiSelectVisible,
@ -141,7 +140,7 @@ export default {
:data-item-iid="item.iid"
:data-item-path="item.referencePath"
:style="cardStyle"
data-testid="board_card"
data-testid="board-card"
class="board-card gl-p-5 gl-rounded-base gl-line-height-normal gl-relative gl-mb-3"
@click="toggleIssue($event)"
>

View File

@ -93,7 +93,7 @@ export default {
}"
:data-list-id="list.id"
class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal is-expandable"
data-qa-selector="board_list"
data-testid="board-list"
>
<div
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"

View File

@ -219,7 +219,7 @@ export default {
<template>
<div
v-cloak
data-qa-selector="boards_list"
data-testid="boards-list"
class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-min-h-0"
>
<gl-alert v-if="errorToDisplay" variant="danger" :dismissible="true" @dismiss="dismissError">

View File

@ -140,7 +140,7 @@ export default {
variant: this.buttonKind,
disabled: this.submitDisabled,
loading: this.isLoading,
'data-qa-selector': 'save_changes_button',
'data-testid': 'save-changes-button',
},
};
},
@ -324,7 +324,7 @@ export default {
ref="name"
v-model="board.name"
class="form-control"
data-qa-selector="board_name_field"
data-testid="board-name-field"
type="text"
:placeholder="$options.i18n.titleFieldPlaceholder"
@keyup.enter="submit"

View File

@ -653,7 +653,7 @@ export default {
<div
v-show="!list.collapsed"
class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column gl-min-h-0"
data-qa-selector="board_list_cards_area"
data-testid="board-list-cards-area"
>
<div
v-if="loading"

View File

@ -365,7 +365,6 @@ export default {
}"
:style="headerStyle"
class="board-header gl-relative"
data-qa-selector="board_list_header"
data-testid="board-list-header"
>
<h3

View File

@ -289,7 +289,6 @@ export default {
v-if="showDropdown"
block
data-testid="boards-dropdown"
data-qa-selector="boards_dropdown"
searchable
:searching="loading"
toggle-class="gl-min-w-20"
@ -322,7 +321,7 @@ export default {
block
class="gl-justify-content-start!"
category="tertiary"
data-qa-selector="create_new_board_button"
data-testid="create-new-board-button"
data-track-action="click_button"
data-track-label="create_new_board"
data-track-property="dropdown"

View File

@ -49,7 +49,7 @@ export default {
v-gl-tooltip
:title="tooltipTitle"
:class="{ 'dot-highlight': hasScope || boardHasScope }"
data-qa-selector="boards_config_button"
data-testid="boards-config-button"
@click.prevent="showPage"
>
{{ buttonText }}

View File

@ -38,7 +38,7 @@ export default {
v-gl-tooltip
category="tertiary"
:icon="isFullscreen ? 'minimize' : 'maximize'"
data-qa-selector="focus_mode_button"
data-testid="focus-mode-button"
:title="$options.i18n.toggleFocusMode"
:aria-label="$options.i18n.toggleFocusMode"
@click="toggleFocusMode"

View File

@ -71,8 +71,7 @@ export default {
size="small"
class="dropdown-header-button gl-p-0!"
icon="close"
data-testid="close-button"
data-qa-selector="close_labels_dropdown_button"
data-testid="close-labels-dropdown-button"
@click="$emit('closeDropdown')"
/>
</div>

View File

@ -144,7 +144,7 @@ export default {
</slot>
</template>
<slot name="default">
<gl-dropdown-form class="gl-relative gl-min-h-7" data-qa-selector="labels_dropdown_content">
<gl-dropdown-form class="gl-relative gl-min-h-7" data-testid="labels-dropdown-content">
<gl-loading-icon
v-if="isLoading"
size="lg"

View File

@ -663,12 +663,6 @@ class Group < Namespace
.non_invite
end
def users_with_parents
User
.where(id: members_with_parents.select(:user_id))
.reorder(nil)
end
def users_with_descendants
User
.where(id: members_with_descendants.select(:user_id))

View File

@ -12,17 +12,19 @@ module WorkItems
end
def self.quick_action_commands
[:set_parent]
[:set_parent, :add_child]
end
def self.quick_action_params
[:set_parent]
[:set_parent, :add_child]
end
def self.process_quick_action_param(param_name, value)
return super unless param_name == :set_parent && value
return super unless param_name.in?(quick_action_params) && value.present?
{ parent: value }
return { parent: value } if param_name == :set_parent
return { children: value } if param_name == :add_child
end
end
end

View File

@ -0,0 +1,8 @@
---
name: fetch_commits_for_bitbucket_server
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133606
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427699
milestone: '16.5'
type: development
group: group::import and integrate
default_enabled: false

View File

@ -0,0 +1,26 @@
---
key_path: redis_hll_counters.quickactions.i_quickactions_add_child_monthly
name: quickactions_add_child_monthly
description: Count of MAU using the `/add_child` quick action
product_section: dev
product_stage: plan
product_group: product_planning
value_type: number
status: active
milestone: "16.5"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132761
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_add_child
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -1,7 +1,7 @@
---
key_path: redis_hll_counters.quickactions.i_quickactions_set_parent_monthly
name: quickactions_set_parent_monthly
description: Count of WAU using the `/set_parent` quick action
description: Count of MAU using the `/set_parent` quick action
product_section: dev
product_stage: plan
product_group: product_planning

View File

@ -0,0 +1,26 @@
---
key_path: redis_hll_counters.quickactions.i_quickactions_add_child_weekly
name: quickactions_add_child_weekly
description: Count of WAU using the `/add_child` quick action
product_section: dev
product_stage: plan
product_group: product_planning
value_type: number
status: active
milestone: "16.5"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132761
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_add_child
performance_indicator_type: []
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate

View File

@ -0,0 +1,10 @@
- title: "Security policy field `newly_detected` is deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters."
removal_milestone: "17.0" # (required) The milestone when this feature is planned to be removed
announcement_milestone: "16.5" # (required) The milestone when this feature was first announced as deprecated.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: g.hickman # (required) GitLab username of the person reporting the change
stage: govern # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/422414 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
In [Support additional filters for scan result policies](https://gitlab.com/groups/gitlab-org/-/epics/6826#note_1341377224), we broke the `newly_detected` field into two options: `new_needs_triage` and `new_dismissed`. By including both options in the security policy YAML, you will achieve the same result as the original `newly_detected` field. However, you may now narrow your filter to ignore findings that have been dismissed by only using `new_needs_triage`.
documentation_url: https://docs.gitlab.com/ee/user/application_security/policies/scan-result-policies.html#scan_finding-rule-type # (optional) This is a link to the current documentation page

View File

@ -844,6 +844,20 @@ Before upgrading to GitLab 17.0, please ensure you have [migrated](https://docs.
<div class="deprecation breaking-change" data-milestone="17.0">
### Security policy field `newly_detected` is deprecated
<div class="deprecation-notes">
- Announced in GitLab <span class="milestone">16.5</span>
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/422414).
</div>
In [Support additional filters for scan result policies](https://gitlab.com/groups/gitlab-org/-/epics/6826#note_1341377224), we broke the `newly_detected` field into two options: `new_needs_triage` and `new_dismissed`. By including both options in the security policy YAML, you will achieve the same result as the original `newly_detected` field. However, you may now narrow your filter to ignore findings that have been dismissed by only using `new_needs_triage`.
</div>
<div class="deprecation breaking-change" data-milestone="17.0">
### Self-managed certificate-based integration with Kubernetes
<div class="deprecation-notes">

View File

@ -119,8 +119,6 @@ To view the value streams dashboard:
You can customize the Value Streams Dashboard and configure what subgroups and projects to include in the page.
A view can display maximum four subgroups or projects.
### Using query parameters
To display multiple subgroups and projects, specify their path as a URL parameter.

View File

@ -230,8 +230,7 @@ table.supported-languages ul {
<li>
<a id="notes-regarding-supported-languages-and-package-managers-2"></a>
<p>
Java 21 LTS is only available when using <a href="https://maven.apache.org/">Maven</a> and is not supported when
<a href="https://docs.gitlab.com/ee/development/fips_compliance.html#enable-fips-mode">FIPS mode</a> is enabled.
Java 21 LTS is only available when using <a href="https://maven.apache.org/">Maven</a> or <a href="https://gradle.org/">Gradle</a>. Java 21 LTS for <a href="https://www.scala-sbt.org/">sbt</a> is not yet available and tracked in <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/421174">issue 421174</a>. It is not supported when <a href="https://docs.gitlab.com/ee/development/fips_compliance.html#enable-fips-mode">FIPS mode</a> is enabled.
</p>
</li>
<li>

View File

@ -38,6 +38,18 @@ Analyzers and their rules are updated [at least monthly](../index.md#vulnerabili
The GitLab ruleset for the Semgrep-based analyzer is managed in [the GitLab-managed open-source `sast-rules` project](https://gitlab.com/gitlab-org/security-products/sast-rules).
When rules are updated, they're released as part of the [Semgrep-based analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep)'s container image.
### Rule update policies
Updates to SAST rules are not [breaking changes](../../../update/terminology.md#breaking-change).
This means that rules may be added, removed, or updated without prior notice.
However, to make rule changes more convenient and understandable, GitLab:
- Documents [rule changes](#important-rule-changes) that are planned or completed.
- [Automatically resolves](index.md#automatic-vulnerability-resolution) findings from rules after they are removed for Semgrep-based analyzers.
- Enables you to [change the status on vulnerabilities where activity = "no longer detected" in bulk](../vulnerability_report/index.md#change-status-of-vulnerabilities).
- Evaluates proposed rule changes for the impact they will have on existing vulnerability records.
## Configure rules in your projects
You should use the default SAST rules unless you have a specific reason to make a change.

View File

@ -146,6 +146,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do
|:--------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Assign one or more users. |
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Assign yourself. |
| `/add_child <work_item>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Add child to `<work_item>`. The `<work_item>` value should be in the format of `#iid`, `group/project#iid`, or a URL to a work item. Multiple work items can be added as children at the same time. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/420797) in GitLab 16.5. |
| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle an emoji reaction. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412275) in GitLab 16.5 |
| `/cc @user` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. [In GitLab 14.9 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/31200), mentioning a user at the start of a line creates a specific type of to-do item notification. |
| `/checkin_reminder <cadence>` | **{dotted-circle}** No| **{check-circle}** Yes | **{dotted-circle}** No | Schedule [check-in reminders](../okrs.md#schedule-okr-check-in-reminders). Options are `weekly`, `twice-monthly`, `monthly`, or `never` (default). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/422761) in GitLab 16.4 with flags named `okrs_mvc` and `okr_checkin_reminders`. |

View File

@ -44,6 +44,10 @@ module BitbucketServer
state == 'merged'
end
def closed?
state == 'closed'
end
def created_at
self.class.convert_timestamp(created_date)
end

View File

@ -20,6 +20,22 @@ module Gitlab
break if pull_requests.empty?
commits_to_fetch = pull_requests.filter_map do |pull_request|
next if already_processed?(pull_request)
next unless pull_request.merged? || pull_request.closed?
[pull_request.source_branch_sha, pull_request.target_branch_sha]
end.flatten
# Bitbucket Server keeps tracks of references for open pull requests in
# refs/heads/pull-requests, but closed and merged requests get moved
# into hidden internal refs under stash-refs/pull-requests. As a result,
# they are not fetched by default.
#
# This method call explicitly fetches head and start commits for affected pull requests.
# That allows us to correctly assign diffs and commits to merge requests.
fetch_missing_commits(commits_to_fetch)
pull_requests.each do |pull_request|
# Needs to come before `already_processed?` as `jobs_remaining` resets to zero when the job restarts and
# jobs_remaining needs to be the total amount of enqueued jobs
@ -42,6 +58,15 @@ module Gitlab
private
def fetch_missing_commits(commits_to_fetch)
return if commits_to_fetch.blank?
return unless Feature.enabled?(:fetch_commits_for_bitbucket_server, project.group)
project.repository.fetch_remote(project.import_url, refmap: commits_to_fetch, prune: false)
rescue StandardError => e
track_import_failure!(project, exception: e)
end
def sidekiq_worker_class
ImportPullRequestWorker
end

View File

@ -36,9 +36,21 @@ module Gitlab
params 'Parent #iid, reference or URL'
condition { supports_parent? && can_admin_link? }
command :set_parent do |parent_param|
@updates[:set_parent] = extract_work_item(parent_param)
@updates[:set_parent] = extract_work_items(parent_param).first
@execution_message[:set_parent] = success_msg[:set_parent]
end
desc { _('Add children to work item') }
explanation do |child_param|
format(_("Add %{child_ref} to this work item as child(ren)."), child_ref: child_param)
end
types WorkItem
params 'Children #iids, references or URLs'
condition { supports_children? && can_admin_link? }
command :add_child do |child_param|
@updates[:add_child] = extract_work_items(child_param)
@execution_message[:add_child] = success_msg[:add_child]
end
end
private
@ -64,15 +76,17 @@ module Gitlab
nil
end
def extract_work_item(params)
# rubocop: disable CodeReuse/ActiveRecord
def extract_work_items(params)
return if params.nil?
issuable_type = params.include?('work_items') ? :work_item : :issue
issuable = extract_references(params, issuable_type).first
return unless issuable
issuables = extract_references(params, issuable_type)
return unless issuables
WorkItem.find(issuable.id)
WorkItem.find(issuables.pluck(:id))
end
# rubocop: enable CodeReuse/ActiveRecord
def validate_promote_to(type)
return error_msg(:not_found, action: 'promote') unless type && supports_promote_to?(type.name)
@ -111,7 +125,8 @@ module Gitlab
{
type: _('Type changed successfully.'),
promote_to: _("Work item promoted successfully."),
set_parent: _('Work item parent set successfully')
set_parent: _('Work item parent set successfully'),
add_child: _('Child work item(s) added successfully')
}
end
@ -119,6 +134,10 @@ module Gitlab
::WorkItems::HierarchyRestriction.find_by_child_type_id(quick_action_target.work_item_type_id).present?
end
def supports_children?
::WorkItems::HierarchyRestriction.find_by_parent_type_id(quick_action_target.work_item_type_id).present?
end
def can_admin_link?
current_user.can?(:admin_issue_link, quick_action_target)
end

View File

@ -2737,6 +2737,9 @@ msgstr ""
msgid "Add \"%{value}\""
msgstr ""
msgid "Add %{child_ref} to this work item as child(ren)."
msgstr ""
msgid "Add %{linkStart}assets%{linkEnd} to your Release. GitLab automatically includes read-only assets, like source code and release evidence."
msgstr ""
@ -2866,6 +2869,9 @@ msgstr ""
msgid "Add child epic to an epic"
msgstr ""
msgid "Add children to work item"
msgstr ""
msgid "Add comment now"
msgstr ""
@ -10047,6 +10053,9 @@ msgstr ""
msgid "Child issues and epics"
msgstr ""
msgid "Child work item(s) added successfully"
msgstr ""
msgid "Chinese language support using"
msgstr ""

View File

@ -6,104 +6,112 @@ module QA
module IssueBoard
class Show < QA::Page::Base
view 'app/assets/javascripts/boards/components/board_card.vue' do
element :board_card
element 'board-card'
end
view 'app/assets/javascripts/boards/components/board_column.vue' do
element 'board-list'
end
view 'app/assets/javascripts/boards/components/board_form.vue' do
element :board_name_field
element :save_changes_button
element 'board-name-field'
element 'save-changes-button'
end
view 'app/assets/javascripts/boards/components/board_list.vue' do
element :board_list_cards_area
element 'board-list-cards-area'
end
view 'app/assets/javascripts/boards/components/board_list_header.vue' do
element 'board-list-header'
end
view 'app/assets/javascripts/boards/components/boards_selector.vue' do
element :boards_dropdown
element :create_new_board_button
element 'boards-dropdown'
element 'create-new-board-button'
end
view 'app/assets/javascripts/boards/components/board_content.vue' do
element :boards_list
element 'boards-list'
end
view 'app/assets/javascripts/boards/components/toggle_focus.vue' do
element :focus_mode_button
element 'focus-mode-button'
end
view 'app/assets/javascripts/boards/components/config_toggle.vue' do
element :boards_config_button
element 'boards-config-button'
end
# The `focused_board` method does not use `find_element` with an element defined
# with the attribute `data-qa-selector` since such element is not unique when the
# with the attribute `data-testid` since such element is not unique when the
# `is-focused` class is not set, and it was not possible to find a better solution.
def focused_board
find('.issue-boards-content.js-focus-mode-board.is-focused')
end
def boards_dropdown
find_element(:boards_dropdown)
find_element('boards-dropdown')
end
def boards_list_cards_area_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_list_cards_area)
within_element_by_index('board-list', index) do
find_element('board-list-cards-area')
end
end
end
def boards_list_header_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_list_header)
within_element_by_index('board-list', index) do
find_element('board-list-header')
end
end
end
def card_of_list_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_card)
within_element_by_index('board-list', index) do
find_element('board-card')
end
end
end
def click_boards_config_button
click_element(:boards_config_button)
click_element('boards-config-button')
wait_for_requests
end
def click_boards_dropdown_button
# The dropdown button comes from the `GlDropdown` component of `@gitlab/ui`,
# so it wasn't possible to add a `data-qa-selector` to it.
find_element(:boards_dropdown).find('button').click
find_element('boards-dropdown').find('button').click
end
def click_focus_mode_button
click_element(:focus_mode_button)
click_element('focus-mode-button')
end
def create_new_board(board_name)
click_boards_dropdown_button
click_element(:create_new_board_button)
click_element('create-new-board-button')
set_name(board_name)
end
def has_modal_board_name_field?
has_element?(:board_name_field, wait: 1)
has_element?('board-name-field', wait: 1)
end
def set_name(name)
find_element(:board_name_field).set(name)
click_element(:save_changes_button)
find_element('board-name-field').set(name)
click_element('save-changes-button')
end
private
def wait_boards_list_finish_loading
within_element(:boards_list) do
within_element('boards-list') do
wait_until(reload: false, max_duration: 5, sleep_interval: 1) do
finished_loading? && (block_given? ? yield : true)
end

View File

@ -12,13 +12,18 @@ module QA
tags: { import_type: ENV["QA_IMPORT_TYPE"], import_repo: ENV["QA_LARGE_IMPORT_REPO"] || "rspec/rspec-core" }
} do
describe 'Project import', product_group: :import_and_integrate do # rubocop:disable RSpec/MultipleMemoizedHelpers
# Full object comparison is a fairly heavy operation
# Importer itself returns counts of objects it fetched and counts it imported
# We can use that for a lightweight comparison for very large projects
let(:only_stats_comparison) { ENV["QA_LARGE_IMPORT_GH_ONLY_STATS_COMPARISON"] == "true" }
let(:github_repo) { ENV['QA_LARGE_IMPORT_REPO'] || 'rspec/rspec-core' }
let(:import_max_duration) { ENV['QA_LARGE_IMPORT_DURATION']&.to_i || 7200 }
let(:api_parallel_threads) { ENV['QA_LARGE_IMPORT_API_PARALLEL']&.to_i || Etc.nprocessors }
let(:logger) { Runtime::Logger.logger }
let(:differ) { RSpec::Support::Differ.new(color: true) }
let(:gitlab_address) { QA::Runtime::Scenario.gitlab_address.chomp("/") }
let(:dummy_url) { "https://example.com" }
let(:dummy_url) { "https://example.com" } # this is used to replace all dynamic urls in descriptions and comments
let(:api_request_params) { { auto_paginate: true, attempts: 2 } }
let(:created_by_pattern) { /\*Created by: \S+\*\n\n/ }
@ -206,95 +211,88 @@ module QA
after do |example|
unless defined?(@import_time)
next save_json(
"data",
{
status: "failed",
importer: :github,
import_finished: false,
import_time: Time.now - @start,
source: {
name: "GitHub",
project_name: github_repo,
address: "https://github.com"
},
target: {
name: "GitLab",
address: gitlab_address
}
}
)
next save_data_json(test_result_data({
status: "failed",
importer: :github,
import_finished: false,
import_time: Time.now - @start
}))
end
# add additional import time metric
example.metadata[:custom_test_metrics][:fields] = { import_time: @import_time }
# save data for comparison notification creation
save_json(
"data",
{
if only_stats_comparison
next save_data_json(test_result_data({
status: example.exception ? "failed" : "passed",
importer: :github,
import_time: @import_time,
import_finished: true,
errors: imported_project.project_import_status[:failed_relations],
reported_stats: @stats,
source: {
name: "GitHub",
project_name: github_repo,
address: "https://github.com",
data: {
branches: gh_branches.length,
commits: gh_commits.length,
labels: gh_labels.length,
milestones: gh_milestones.length,
mrs: gh_prs.length,
mr_comments: gh_prs.sum { |_k, v| v[:comments].length },
mr_events: gh_prs.sum { |_k, v| v[:events].length },
issues: gh_issues.length,
issue_comments: gh_issues.sum { |_k, v| v[:comments].length },
issue_events: gh_issues.sum { |_k, v| v[:events].length }
}
},
target: {
name: "GitLab",
project_name: imported_project.path_with_namespace,
address: gitlab_address,
data: {
branches: gl_branches.length,
commits: gl_commits.length,
labels: gl_labels.length,
milestones: gl_milestones.length,
mrs: mrs.length,
mr_comments: mrs.sum { |_k, v| v[:comments].length },
mr_events: mrs.sum { |_k, v| v[:events].length },
issues: gl_issues.length,
issue_comments: gl_issues.sum { |_k, v| v[:comments].length },
issue_events: gl_issues.sum { |_k, v| v[:events].length }
}
},
not_imported: {
mrs: @mr_diff,
issues: @issue_diff
reported_stats: @stats
}))
end
save_data_json(test_result_data({
status: example.exception ? "failed" : "passed",
import_time: @import_time,
import_finished: true,
errors: imported_project.project_import_status[:failed_relations],
reported_stats: @stats,
source: {
data: {
branches: gh_branches.length,
commits: gh_commits.length,
labels: gh_labels.length,
milestones: gh_milestones.length,
mrs: gh_prs.length,
mr_comments: gh_prs.sum { |_k, v| v[:comments].length },
mr_events: gh_prs.sum { |_k, v| v[:events].length },
issues: gh_issues.length,
issue_comments: gh_issues.sum { |_k, v| v[:comments].length },
issue_events: gh_issues.sum { |_k, v| v[:events].length }
}
},
target: {
project_name: imported_project.path_with_namespace,
data: {
branches: gl_branches.length,
commits: gl_commits.length,
labels: gl_labels.length,
milestones: gl_milestones.length,
mrs: mrs.length,
mr_comments: mrs.sum { |_k, v| v[:comments].length },
mr_events: mrs.sum { |_k, v| v[:events].length },
issues: gl_issues.length,
issue_comments: gl_issues.sum { |_k, v| v[:comments].length },
issue_events: gl_issues.sum { |_k, v| v[:events].length }
}
},
not_imported: {
mrs: @mr_diff,
issues: @issue_diff
}
)
}))
end
it(
'imports large Github repo via api',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347668'
) do
if only_stats_comparison
logger.warn("Test is running in lightweight comparison mode, only object counts will be compared!")
end
@start = Time.now
# trigger import and log project paths
logger.info("== Triggering import of project '#{github_repo}' in to '#{imported_project.reload!.full_path}' ==")
# fetch all objects right after import has started
fetch_github_objects
fetch_github_objects unless only_stats_comparison
import_status = -> {
imported_project.project_import_status.yield_self do |status|
@stats = status.dig(:stats, :imported)
@stats = status[:stats]&.slice(:fetched, :imported)
# fail fast if import explicitly failed
raise "Import of '#{imported_project.full_path}' failed!" if status[:import_status] == 'failed'
@ -308,15 +306,38 @@ module QA
@import_time = Time.now - @start
aggregate_failures do
verify_repository_import
verify_labels_import
verify_milestones_import
verify_merge_requests_import
verify_issues_import
if only_stats_comparison
expect(@stats[:fetched]).to eq(@stats[:imported])
else
aggregate_failures do
verify_repository_import
verify_labels_import
verify_milestones_import
verify_merge_requests_import
verify_issues_import
end
end
end
# Base test result data used for test result reporting
#
# @param [Hash] additional_data
# @return [Hash]
def test_result_data(additional_data = {})
{
importer: :github,
source: {
name: "GitHub",
project_name: github_repo,
address: "https://github.com"
},
target: {
name: "GitLab",
address: gitlab_address
}
}.deep_merge(additional_data)
end
# Persist all objects from repository being imported
#
# @return [void]
@ -634,11 +655,10 @@ module QA
# Save json as file
#
# @param [String] name
# @param [Hash] json
# @return [void]
def save_json(name, json)
File.open("tmp/#{name}.json", "w") { |file| file.write(JSON.pretty_generate(json)) }
def save_data_json(json)
File.open("tmp/github-import-data.json", "w") { |file| file.write(JSON.pretty_generate(json)) }
end
# Extract id number from web url of issue or pull request

View File

@ -78,7 +78,6 @@ module QA
after do |example|
unless defined?(@import_time)
next save_json(
"data",
{
status: "failed",
importer: :gitlab,
@ -101,7 +100,6 @@ module QA
example.metadata[:custom_test_metrics][:fields] = { import_time: @import_time }
# save data for comparison notification creation
save_json(
"data",
{
status: example.exception ? "failed" : "passed",
importer: :gitlab,
@ -429,11 +427,10 @@ module QA
# Save json as file
#
# @param [String] name
# @param [Hash] json
# @return [void]
def save_json(name, json)
File.open("tmp/#{name}.json", "w") { |file| file.write(JSON.pretty_generate(json)) }
def save_json(json)
File.open("tmp/gitlab-import-data.json", "w") { |file| file.write(JSON.pretty_generate(json)) }
end
end
end

View File

@ -153,6 +153,8 @@ begin
sleep 1
end
rescue Interrupt
# Quietly shut down
ensure
print "\e[?1049l" # Restores the original screen buffer
print "\e[H" # Moves the cursor home

View File

@ -7,7 +7,7 @@ RSpec.describe 'Issue boards sidebar labels select', :js, feature_category: :tea
include_context 'labels from nested groups and projects'
let(:card) { find('.board:nth-child(1)').first('[data-testid="board_card"]') }
let(:card) { find('.board:nth-child(1)').first('[data-testid="board-card"]') }
context 'group boards' do
context 'in the top-level group board' do

View File

@ -28,7 +28,7 @@ RSpec.describe 'Project issue boards sidebar', :js, feature_category: :team_plan
it_behaves_like 'issue boards sidebar'
def first_card
find('.board:nth-child(1)').first("[data-testid='board_card']")
find('.board:nth-child(1)').first("[data-testid='board-card']")
end
def click_first_issue_card

View File

@ -53,7 +53,7 @@ RSpec.describe 'User visits issue boards', :js, feature_category: :team_planning
it 'displays all issues satisfiying filter params and correctly sets url params' do
expect(page).to have_current_path(board_path)
page.assert_selector('[data-testid="board_card"]', count: expected_issues.length)
page.assert_selector('[data-testid="board-card"]', count: expected_issues.length)
expected_issues.each { |issue_title| expect(page).to have_link issue_title }
end
end

View File

@ -82,6 +82,18 @@ RSpec.describe BitbucketServer::Representation::PullRequest, feature_category: :
it { expect(subject.merged?).to be_truthy }
end
describe '#closed?' do
it { expect(subject.closed?).to be_falsey }
context 'for declined pull requests' do
before do
sample_data['state'] = 'DECLINED'
end
it { expect(subject.closed?).to be_truthy }
end
end
describe '#created_at' do
it { expect(subject.created_at.to_i).to eq(sample_data['createdDate'] / 1000) }
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::BitbucketServerImport::Importers::PullRequestsImporter, feature_category: :importers do
let_it_be(:project) do
create(:project, :import_started,
create(:project, :with_import_url, :import_started, :empty_repo,
import_data_attributes: {
data: { 'project_key' => 'key', 'repo_slug' => 'slug' },
credentials: { 'base_uri' => 'http://bitbucket.org/', 'user' => 'bitbucket', 'password' => 'password' }
@ -19,8 +19,30 @@ RSpec.describe Gitlab::BitbucketServerImport::Importers::PullRequestsImporter, f
allow_next_instance_of(BitbucketServer::Client) do |client|
allow(client).to receive(:pull_requests).and_return(
[
BitbucketServer::Representation::PullRequest.new({ 'id' => 1 }),
BitbucketServer::Representation::PullRequest.new({ 'id' => 2 })
BitbucketServer::Representation::PullRequest.new(
{
'id' => 1,
'state' => 'MERGED',
'fromRef' => { 'latestCommit' => 'aaaa1' },
'toRef' => { 'latestCommit' => 'aaaa2' }
}
),
BitbucketServer::Representation::PullRequest.new(
{
'id' => 2,
'state' => 'DECLINED',
'fromRef' => { 'latestCommit' => 'bbbb1' },
'toRef' => { 'latestCommit' => 'bbbb2' }
}
),
BitbucketServer::Representation::PullRequest.new(
{
'id' => 3,
'state' => 'OPEN',
'fromRef' => { 'latestCommit' => 'cccc1' },
'toRef' => { 'latestCommit' => 'cccc2' }
}
)
],
[]
)
@ -28,14 +50,14 @@ RSpec.describe Gitlab::BitbucketServerImport::Importers::PullRequestsImporter, f
end
it 'imports each pull request in parallel', :aggregate_failures do
expect(Gitlab::BitbucketServerImport::ImportPullRequestWorker).to receive(:perform_in).twice
expect(Gitlab::BitbucketServerImport::ImportPullRequestWorker).to receive(:perform_in).thrice
waiter = importer.execute
expect(waiter).to be_an_instance_of(Gitlab::JobWaiter)
expect(waiter.jobs_remaining).to eq(2)
expect(waiter.jobs_remaining).to eq(3)
expect(Gitlab::Cache::Import::Caching.values_from_set(importer.already_processed_cache_key))
.to match_array(%w[1 2])
.to match_array(%w[1 2 3])
end
context 'when pull request was already processed' do
@ -44,12 +66,68 @@ RSpec.describe Gitlab::BitbucketServerImport::Importers::PullRequestsImporter, f
end
it 'does not schedule job for processed pull requests', :aggregate_failures do
expect(Gitlab::BitbucketServerImport::ImportPullRequestWorker).to receive(:perform_in).once
expect(Gitlab::BitbucketServerImport::ImportPullRequestWorker).to receive(:perform_in).twice
waiter = importer.execute
expect(waiter).to be_an_instance_of(Gitlab::JobWaiter)
expect(waiter.jobs_remaining).to eq(2)
expect(waiter.jobs_remaining).to eq(3)
end
end
context 'when pull requests are in merged or declined status' do
it 'fetches latest commits from the remote repository' do
expect(project.repository).to receive(:fetch_remote).with(
project.import_url,
refmap: %w[aaaa1 aaaa2 bbbb1 bbbb2],
prune: false
)
importer.execute
end
context 'when feature flag "fetch_commits_for_bitbucket_server" is disabled' do
before do
stub_feature_flags(fetch_commits_for_bitbucket_server: false)
end
it 'does not fetch anything' do
expect(project.repository).not_to receive(:fetch_remote)
importer.execute
end
end
context 'when there are no commits to process' do
before do
Gitlab::Cache::Import::Caching.set_add(importer.already_processed_cache_key, 1)
Gitlab::Cache::Import::Caching.set_add(importer.already_processed_cache_key, 2)
end
it 'does not fetch anything' do
expect(project.repository).not_to receive(:fetch_remote)
importer.execute
end
end
context 'when fetch process is failed' do
let(:exception) { ArgumentError.new('blank or empty URL') }
before do
allow(project.repository).to receive(:fetch_remote).and_raise(exception)
end
it 'rescues and logs the exception' do
expect(Gitlab::Import::ImportFailureService)
.to receive(:track)
.with(
project_id: project.id,
exception: exception,
error_source: described_class.name
).and_call_original
importer.execute
end
end
end
end

View File

@ -334,6 +334,46 @@ RSpec.describe Notes::QuickActionsService, feature_category: :team_planning do
end
end
describe '/add_child' do
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
let_it_be_with_reload(:child) { create(:work_item, :objective, project: project) }
let_it_be_with_reload(:second_child) { create(:work_item, :objective, project: project) }
let_it_be(:note_text) { "/add_child #{child.to_reference}, #{second_child.to_reference}" }
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
let_it_be(:children) { [child, second_child] }
shared_examples 'adds child work items' do
it 'leaves the note empty' do
expect(execute(note)).to be_empty
end
it 'adds child work items' do
execute(note)
expect(noteable.valid?).to be_truthy
expect(noteable.work_item_children).to eq(children)
end
end
context 'when using work item reference' do
let_it_be(:note_text) { "/add_child #{child.to_reference(full: true)},#{second_child.to_reference(full: true)}" }
it_behaves_like 'adds child work items'
end
context 'when using work item iid' do
it_behaves_like 'adds child work items'
end
context 'when using work item URL' do
let_it_be(:project_path) { "#{Gitlab.config.gitlab.url}/#{project.full_path}" }
let_it_be(:url) { "#{project_path}/work_items/#{child.iid},#{project_path}/work_items/#{second_child.iid}" }
let_it_be(:note_text) { "/add_child #{url}" }
it_behaves_like 'adds child work items'
end
end
describe '/set_parent' do
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }

View File

@ -3091,6 +3091,55 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
it_behaves_like 'command is not available'
end
end
describe '/add_child command' do
let_it_be(:child) { create(:work_item, :issue, project: project) }
let_it_be(:work_item) { create(:work_item, :objective, project: project) }
let_it_be(:child_ref) { child.to_reference(project) }
let(:command) { "/add_child #{child_ref}" }
shared_examples 'command is available' do
it 'explanation contains correct message' do
_, explanations = service.explain(command, work_item)
expect(explanations)
.to contain_exactly("Add #{child_ref} to this work item as child(ren).")
end
it 'contains command' do
expect(service.available_commands(work_item)).to include(a_hash_including(name: :add_child))
end
end
shared_examples 'command is not available' do
it 'explanation is empty' do
_, explanations = service.explain(command, work_item)
expect(explanations).to eq([])
end
it 'does not contain command' do
expect(service.available_commands(work_item)).not_to include(a_hash_including(name: :add_child))
end
end
context 'when user can admin link' do
it_behaves_like 'command is available'
context 'when work item type does not support children' do
let_it_be(:work_item) { build(:work_item, :key_result, project: project) }
it_behaves_like 'command is not available'
end
end
context 'when user cannot admin link' do
subject(:service) { described_class.new(project, create(:user)) }
it_behaves_like 'command is not available'
end
end
end
describe '#available_commands' do