Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/integrations/gitlab_slack_application/components/gitlab_slack_application.vue2
-rw-r--r--app/assets/javascripts/integrations/gitlab_slack_application/components/projects_dropdown.vue65
-rw-r--r--app/models/merge_request.rb12
-rw-r--r--app/services/merge_requests/approval_service.rb13
-rw-r--r--doc/api/graphql/reference/index.md73
-rw-r--r--doc/ci/migration/jenkins.md4
-rw-r--r--doc/ci/pipelines/downstream_pipelines.md3
-rw-r--r--doc/ci/yaml/index.md43
-rw-r--r--doc/development/bulk_import.md6
-rw-r--r--doc/update/versions/gitlab_15_changes.md3
-rw-r--r--lib/gitlab/database/load_balancing/service_discovery.rb8
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/contributors/component/contributors_spec.js15
-rw-r--r--spec/frontend/integrations/gitlab_slack_application/components/projects_dropdown_spec.js54
-rw-r--r--spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb21
-rw-r--r--spec/models/merge_request_spec.rb53
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb33
-rw-r--r--storybook/config/addons/gitlab_api_access/manager.js20
-rw-r--r--storybook/config/webpack.config.js19
-rw-r--r--storybook/fixture_stub.js12
21 files changed, 289 insertions, 175 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index dd7914a7999..dd2a5654d2c 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-9d00c51754d1199119e46d68db3b233115118afa
+0fed39f4d44b51ff5dfa20e6dc4a1a940a5a6b38
diff --git a/app/assets/javascripts/integrations/gitlab_slack_application/components/gitlab_slack_application.vue b/app/assets/javascripts/integrations/gitlab_slack_application/components/gitlab_slack_application.vue
index bcb199853bd..edfb0af5bbe 100644
--- a/app/assets/javascripts/integrations/gitlab_slack_application/components/gitlab_slack_application.vue
+++ b/app/assets/javascripts/integrations/gitlab_slack_application/components/gitlab_slack_application.vue
@@ -101,7 +101,7 @@ export default {
@project-selected="selectProject"
/>
- <div class="gl-display-flex gl-justify-content-end">
+ <div class="gl-display-flex gl-justify-content-end gl-mt-3">
<gl-button
category="primary"
variant="confirm"
diff --git a/app/assets/javascripts/integrations/gitlab_slack_application/components/projects_dropdown.vue b/app/assets/javascripts/integrations/gitlab_slack_application/components/projects_dropdown.vue
index 26d191cd0bf..7c5287c69d6 100644
--- a/app/assets/javascripts/integrations/gitlab_slack_application/components/projects_dropdown.vue
+++ b/app/assets/javascripts/integrations/gitlab_slack_application/components/projects_dropdown.vue
@@ -1,13 +1,13 @@
<script>
-import { GlDropdown } from '@gitlab/ui';
+import { GlCollapsibleListbox, GlAvatarLabeled } from '@gitlab/ui';
import { __ } from '~/locale';
-
-import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
+import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default {
components: {
- GlDropdown,
- ProjectListItem,
+ GlCollapsibleListbox,
+ GlAvatarLabeled,
},
props: {
projectDropdownText: {
@@ -26,30 +26,61 @@ export default {
default: null,
},
},
+ data() {
+ return {
+ selected: this.selectedProject,
+ };
+ },
computed: {
dropdownText() {
return this.selectedProject
? this.selectedProject.name_with_namespace
: this.projectDropdownText;
},
+ items() {
+ const items = this.projects.map((project) => {
+ return {
+ value: project.id,
+ ...project,
+ };
+ });
+
+ return convertObjectPropsToCamelCase(items, { deep: true });
+ },
},
methods: {
- onClick(project) {
- this.$emit('project-selected', project);
- this.$refs.dropdown.hide(true);
+ getEntityId(project) {
+ return isGid(project.id) ? getIdFromGraphQLId(project.id) : project.id;
+ },
+ selectProject(projectId) {
+ this.$emit(
+ 'project-selected',
+ this.projects.find((project) => project.id === projectId),
+ );
},
},
};
</script>
<template>
- <gl-dropdown ref="dropdown" block :text="dropdownText" menu-class="gl-w-full!">
- <project-list-item
- v-for="project in projects"
- :key="project.id"
- :project="project"
- :selected="false"
- @click="onClick(project)"
- />
- </gl-dropdown>
+ <gl-collapsible-listbox
+ v-model="selected"
+ block
+ fluid-width
+ is-check-centered
+ :toggle-text="dropdownText"
+ :items="items"
+ @select="selectProject"
+ >
+ <template #list-item="{ item }">
+ <gl-avatar-labeled
+ :label="item.nameWithNamespace"
+ :entity-name="item.nameWithNamespace"
+ :entity-id="getEntityId(item)"
+ shape="rect"
+ :size="32"
+ :src="item.avatarUrl"
+ />
+ </template>
+ </gl-collapsible-listbox>
</template>
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6a72ed6476e..65264596978 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -2115,6 +2115,18 @@ class MergeRequest < ApplicationRecord
!squash && target_project.squash_always?
end
+ def current_patch_id_sha
+ return merge_request_diff.patch_id_sha if merge_request_diff.patch_id_sha.present?
+
+ base_sha = diff_refs&.base_sha
+ head_sha = diff_refs&.head_sha
+
+ return unless base_sha && head_sha
+ return if base_sha == head_sha
+
+ project.repository.get_patch_id(base_sha, head_sha)
+ end
+
private
attr_accessor :skip_fetch_ref
diff --git a/app/services/merge_requests/approval_service.rb b/app/services/merge_requests/approval_service.rb
index dbe5567cbc5..f9857cdad39 100644
--- a/app/services/merge_requests/approval_service.rb
+++ b/app/services/merge_requests/approval_service.rb
@@ -7,7 +7,7 @@ module MergeRequests
approval = merge_request.approvals.new(
user: current_user,
- patch_id_sha: fetch_patch_id_sha(merge_request)
+ patch_id_sha: merge_request.current_patch_id_sha
)
return success unless save_approval(approval)
@@ -36,17 +36,6 @@ module MergeRequests
private
- def fetch_patch_id_sha(merge_request)
- diff_refs = merge_request.diff_refs
- base_sha = diff_refs&.base_sha
- head_sha = diff_refs&.head_sha
-
- return unless base_sha && head_sha
- return if base_sha == head_sha
-
- merge_request.project.repository.get_patch_id(base_sha, head_sha)
- end
-
def eligible_for_approval?(merge_request)
merge_request.eligible_for_approval_by?(current_user)
end
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 6871aaada12..428c0830d83 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -80,7 +80,7 @@ WARNING:
**Introduced** in 16.1.
This feature is an Experiment. It can be changed or removed at any time.
-Returns [`AiChatMessageConnection!`](#aichatmessageconnection).
+Returns [`AiMessageConnection!`](#aimessageconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
@@ -91,7 +91,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryaimessagesrequestids"></a>`requestIds` | [`[ID!]`](#id) | Array of request IDs to fetch. |
-| <a id="queryaimessagesroles"></a>`roles` | [`[AiChatMessageRole!]`](#aichatmessagerole) | Array of roles to fetch. |
+| <a id="queryaimessagesroles"></a>`roles` | [`[AiMessageRole!]`](#aimessagerole) | Array of roles to fetch. |
### `Query.auditEventDefinitions`
@@ -7951,28 +7951,28 @@ The edge type for [`AgentConfiguration`](#agentconfiguration).
| <a id="agentconfigurationedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="agentconfigurationedgenode"></a>`node` | [`AgentConfiguration`](#agentconfiguration) | The item at the end of the edge. |
-#### `AiChatMessageConnection`
+#### `AiMessageConnection`
-The connection type for [`AiChatMessage`](#aichatmessage).
+The connection type for [`AiMessage`](#aimessage).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="aichatmessageconnectionedges"></a>`edges` | [`[AiChatMessageEdge]`](#aichatmessageedge) | A list of edges. |
-| <a id="aichatmessageconnectionnodes"></a>`nodes` | [`[AiChatMessage]`](#aichatmessage) | A list of nodes. |
-| <a id="aichatmessageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+| <a id="aimessageconnectionedges"></a>`edges` | [`[AiMessageEdge]`](#aimessageedge) | A list of edges. |
+| <a id="aimessageconnectionnodes"></a>`nodes` | [`[AiMessage]`](#aimessage) | A list of nodes. |
+| <a id="aimessageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
-#### `AiChatMessageEdge`
+#### `AiMessageEdge`
-The edge type for [`AiChatMessage`](#aichatmessage).
+The edge type for [`AiMessage`](#aimessage).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="aichatmessageedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
-| <a id="aichatmessageedgenode"></a>`node` | [`AiChatMessage`](#aichatmessage) | The item at the end of the edge. |
+| <a id="aimessageedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="aimessageedgenode"></a>`node` | [`AiMessage`](#aimessage) | The item at the end of the edge. |
#### `AlertManagementAlertConnection`
@@ -13127,22 +13127,24 @@ Information about a connected Agent.
| <a id="agentmetadatapodnamespace"></a>`podNamespace` | [`String`](#string) | Namespace of the pod running the Agent. |
| <a id="agentmetadataversion"></a>`version` | [`String`](#string) | Agent version tag. |
-### `AiChatMessage`
+### `AiMessage`
-GitLab Duo Chat message.
+AI features communication message.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="aichatmessagecontent"></a>`content` | [`String`](#string) | Content of the message. Can be null for failed responses. |
-| <a id="aichatmessagecontenthtml"></a>`contentHtml` | [`String`](#string) | Content of the message in HTML format. Can be null for failed responses. |
-| <a id="aichatmessageerrors"></a>`errors` | [`[String!]!`](#string) | Errors that occurred while asynchronously fetching an AI (assistant) response. |
-| <a id="aichatmessageextras"></a>`extras` | [`AiMessageExtras`](#aimessageextras) | Extra message metadata. |
-| <a id="aichatmessageid"></a>`id` | [`ID`](#id) | UUID of the message. |
-| <a id="aichatmessagerequestid"></a>`requestId` | [`ID`](#id) | UUID of the original request message. Shared between chat prompt and response. |
-| <a id="aichatmessagerole"></a>`role` | [`AiChatMessageRole!`](#aichatmessagerole) | Message role. |
-| <a id="aichatmessagetimestamp"></a>`timestamp` | [`Time!`](#time) | Message timestamp. |
+| <a id="aimessagechunkid"></a>`chunkId` | [`Int`](#int) | Incremental ID for a chunk from a streamed message. Null when it is not a streamed message. |
+| <a id="aimessagecontent"></a>`content` | [`String`](#string) | Raw response content. |
+| <a id="aimessagecontenthtml"></a>`contentHtml` | [`String`](#string) | Response content as HTML. |
+| <a id="aimessageerrors"></a>`errors` | [`[String!]`](#string) | Message errors. |
+| <a id="aimessageextras"></a>`extras` | [`AiMessageExtras`](#aimessageextras) | Extra message metadata. |
+| <a id="aimessageid"></a>`id` | [`ID`](#id) | UUID of the message. |
+| <a id="aimessagerequestid"></a>`requestId` | [`String`](#string) | UUID of the original request. Shared between chat prompt and response. |
+| <a id="aimessagerole"></a>`role` | [`AiMessageRole!`](#aimessagerole) | Message owner role. |
+| <a id="aimessagetimestamp"></a>`timestamp` | [`Time!`](#time) | Message creation timestamp. |
+| <a id="aimessagetype"></a>`type` | [`AiMessageType`](#aimessagetype) | Message type. |
### `AiMessageExtras`
@@ -13154,25 +13156,6 @@ Extra metadata for AI message.
| ---- | ---- | ----------- |
| <a id="aimessageextrassources"></a>`sources` | [`[JSON!]`](#json) | Sources used to form the message. |
-### `AiResponse`
-
-#### Fields
-
-| Name | Type | Description |
-| ---- | ---- | ----------- |
-| <a id="airesponsechunkid"></a>`chunkId` | [`Int`](#int) | Incremental ID for a chunk from a streamed response. Null when it is not a streamed response. |
-| <a id="airesponsecontent"></a>`content` | [`String`](#string) | Raw response content. |
-| <a id="airesponsecontenthtml"></a>`contentHtml` | [`String`](#string) | Response content as HTML. |
-| <a id="airesponseerrors"></a>`errors` | [`[String!]`](#string) | Errors return by AI API as response. |
-| <a id="airesponseextras"></a>`extras` | [`AiMessageExtras`](#aimessageextras) | Extra message metadata. |
-| <a id="airesponseid"></a>`id` | [`ID`](#id) | UUID of the message. |
-| <a id="airesponserequestid"></a>`requestId` | [`String`](#string) | ID of the original request. |
-| <a id="airesponseresponsebody"></a>`responseBody` **{warning-solid}** | [`String`](#string) | **Deprecated** in 16.4. Moved to content attribute. |
-| <a id="airesponseresponsebodyhtml"></a>`responseBodyHtml` **{warning-solid}** | [`String`](#string) | **Deprecated** in 16.4. Moved to contentHtml attribute. |
-| <a id="airesponserole"></a>`role` | [`AiChatMessageRole!`](#aichatmessagerole) | Message role. |
-| <a id="airesponsetimestamp"></a>`timestamp` | [`Time!`](#time) | Message timestamp. |
-| <a id="airesponsetype"></a>`type` | [`AiMessageType`](#aimessagetype) | Message type. |
-
### `AlertManagementAlert`
Describes an alert from the project's Alert Management.
@@ -26758,15 +26741,15 @@ Agent token statuses.
| <a id="agenttokenstatusactive"></a>`ACTIVE` | Active agent token. |
| <a id="agenttokenstatusrevoked"></a>`REVOKED` | Revoked agent token. |
-### `AiChatMessageRole`
+### `AiMessageRole`
-Roles to filter in chat message.
+Possible message roles for AI features.
| Value | Description |
| ----- | ----------- |
-| <a id="aichatmessageroleassistant"></a>`ASSISTANT` | Filter only assistant messages. |
-| <a id="aichatmessagerolesystem"></a>`SYSTEM` | Filter only system messages. |
-| <a id="aichatmessageroleuser"></a>`USER` | Filter only user messages. |
+| <a id="aimessageroleassistant"></a>`ASSISTANT` | assistant message. |
+| <a id="aimessagerolesystem"></a>`SYSTEM` | system message. |
+| <a id="aimessageroleuser"></a>`USER` | user message. |
### `AiMessageType`
diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md
index 5d6f3359daa..0af1cc84878 100644
--- a/doc/ci/migration/jenkins.md
+++ b/doc/ci/migration/jenkins.md
@@ -119,13 +119,13 @@ shared configuration in a hidden job:
test-job:
extends:
- - .docker-config
+ - .test-config
script:
- bundle exec rake rspec
lint-job:
extends:
- - .docker-config
+ - .test-config
script:
- yarn run prettier
```
diff --git a/doc/ci/pipelines/downstream_pipelines.md b/doc/ci/pipelines/downstream_pipelines.md
index 6dc58882f37..21ed66ef939 100644
--- a/doc/ci/pipelines/downstream_pipelines.md
+++ b/doc/ci/pipelines/downstream_pipelines.md
@@ -68,9 +68,6 @@ Multi-project pipelines:
- Are visible in the downstream project's pipeline list.
- Are independent, so there are no nesting limits.
-For more information, see the **Cross-project Pipeline Triggering and Visualization** demo at
-[GitLab@learn](https://about.gitlab.com/learn/) in the **Continuous Integration** section.
-
If you use a public project to trigger downstream pipelines in a private project,
make sure there are no confidentiality problems. The upstream project's pipelines page
always displays:
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index ddb40d9f435..403152b32a9 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -7,13 +7,14 @@ type: reference
# `.gitlab-ci.yml` keyword reference **(FREE ALL)**
-This document lists the configuration options for your GitLab `.gitlab-ci.yml` file.
+This document lists the configuration options for the GitLab `.gitlab-ci.yml` file.
+This file is where you define the CI/CD jobs that make up your pipeline.
+- To create your own `.gitlab-ci.yml` file, try a tutorial that demonstrates a
+ [simple](../quick_start/index.md) or [complex](../quick_start/tutorial.md) pipeline.
- For a collection of examples, see [GitLab CI/CD examples](../examples/index.md).
- To view a large `.gitlab-ci.yml` file used in an enterprise, see the
[`.gitlab-ci.yml` file for `gitlab`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab-ci.yml).
-- To create your own `.gitlab-ci.yml` file, try a tutorial that demonstrates a
- [simple](../quick_start/index.md) or [complex](../quick_start/tutorial.md) pipeline.
When you are editing your `.gitlab-ci.yml` file, you can validate it with the
[CI Lint](../lint.md) tool.
@@ -2768,8 +2769,8 @@ The `linux:rspec` job runs as soon as the `linux:build: [aws, app1]` job finishe
### `only` / `except`
NOTE:
-`only` and `except` are not being actively developed. [`rules`](#rules) is the preferred
-keyword to control when to add jobs to pipelines.
+`only` and `except` are not being actively developed. To control when to add jobs to pipelines,
+use [`rules`](#rules) instead.
You can use `only` and `except` to control when to add jobs to pipelines.
@@ -2781,13 +2782,13 @@ for more details and examples.
#### `only:refs` / `except:refs`
+NOTE:
+`only:refs` and `except:refs` are not being actively developed. To use refs, regular expressions,
+or variables to control when to add jobs to pipelines, use [`rules:if`](#rulesif) instead.
+
Use the `only:refs` and `except:refs` keywords to control when to add jobs to a
pipeline based on branch names or pipeline types.
-`only:refs` and `except:refs` are not being actively developed. [`rules:if`](#rulesif)
-is the preferred keyword when using refs, regular expressions, or variables to control
-when to add jobs to pipelines.
-
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**: An array including any number of:
@@ -2870,13 +2871,13 @@ job2:
#### `only:variables` / `except:variables`
+NOTE:
+`only:variables` and `except:variables` are not being actively developed. To use refs,
+regular expressions, or variables to control when to add jobs to pipelines, use [`rules:if`](#rulesif) instead.
+
Use the `only:variables` or `except:variables` keywords to control when to add jobs
to a pipeline, based on the status of [CI/CD variables](../variables/index.md).
-`only:variables` and `except:variables` are not being actively developed. [`rules:if`](#rulesif)
-is the preferred keyword when using refs, regular expressions, or variables to control
-when to add jobs to pipelines.
-
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**:
@@ -2900,6 +2901,10 @@ deploy:
#### `only:changes` / `except:changes`
+NOTE:
+`only:changes` and `except:changes` are not being actively developed. To use changed files
+to control when to add a job to a pipeline, use [`rules:changes`](#ruleschanges) instead.
+
Use the `changes` keyword with `only` to run a job, or with `except` to skip a job,
when a Git push event modifies a file.
@@ -2909,9 +2914,6 @@ Use `changes` in pipelines with the following refs:
- `external_pull_requests`
- `merge_requests` (see additional details about [using `only:changes` with merge request pipelines](../jobs/job_control.md#use-onlychanges-with-merge-request-pipelines))
-`only:changes` and `except:changes` are not being actively developed. [`rules:changes`](#ruleschanges)
-is the preferred keyword when using changed files to control when to add jobs to pipelines.
-
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**: An array including any number of:
@@ -2959,13 +2961,14 @@ docker build:
#### `only:kubernetes` / `except:kubernetes`
+NOTE:
+`only:refs` and `except:refs` are not being actively developed. To control if jobs are added
+to the pipeline when the Kubernetes service is active in the project, use [`rules:if`](#rulesif)
+with the [`CI_KUBERNETES_ACTIVE`](../variables/predefined_variables.md) predefined CI/CD variable instead.
+
Use `only:kubernetes` or `except:kubernetes` to control if jobs are added to the pipeline
when the Kubernetes service is active in the project.
-`only:refs` and `except:refs` are not being actively developed. Use [`rules:if`](#rulesif)
-with the [`CI_KUBERNETES_ACTIVE`](../variables/predefined_variables.md) predefined CI/CD variable
-to control if jobs are added to the pipeline when the Kubernetes service is active in the project.
-
**Keyword type**: Job-specific. You can use it only as part of a job.
**Possible inputs**:
diff --git a/doc/development/bulk_import.md b/doc/development/bulk_import.md
index 304aab9e3b3..081af2b4e17 100644
--- a/doc/development/bulk_import.md
+++ b/doc/development/bulk_import.md
@@ -6,7 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Group migration by direct transfer
-[Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2771) in GitLab 13.7.
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2771) in GitLab 13.7.
+
+NOTE:
+To use direct transfer, ensure your GitLab installation is accessible from
+[GitLab IP addresses](../user/gitlab_com/index.md#ip-range) and has a public DNS entry.
[Group migration by direct transfer](../user/group/import/index.md#migrate-groups-by-direct-transfer-recommended) is the
evolution of migrating groups and projects using file exports. The goal is to have an easier way for the user to migrate a whole group,
diff --git a/doc/update/versions/gitlab_15_changes.md b/doc/update/versions/gitlab_15_changes.md
index 12cbf5c5ec1..997397904dd 100644
--- a/doc/update/versions/gitlab_15_changes.md
+++ b/doc/update/versions/gitlab_15_changes.md
@@ -144,6 +144,9 @@ if you can't upgrade to 15.11.12 and later.
- This issue may not manifest immediately as it can take up to a week before the Sidekiq is saturated enough.
- Elasticsearch does not need to be enabled for this to occur.
- To resolve this issue, upgrade to 15.11 or use the workaround in the issue.
+- A bug with the [`BackfillTraversalIdsToBlobsAndWikiBlobs` advanced search migration](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107730) might cause the Elasticsearch cluster to become saturated.
+ - When this issue occurs, searches might become slow and updates to the Elasticsearch cluster might take a long time to complete.
+ - To resolve this issue, upgrade to GitLab 15.10 to [reduce the migration batch size](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113719).
- **Upgrade to patch release 15.9.3 or later**. This provides fixes for two database migration bugs:
- Patch releases 15.9.0, 15.9.1, 15.9.2 have a bug that can cause data loss
from the user profile fields `linkedin`, `twitter`, `skype`, `website_url`,
diff --git a/lib/gitlab/database/load_balancing/service_discovery.rb b/lib/gitlab/database/load_balancing/service_discovery.rb
index 55926e486a4..3d11f0f88c1 100644
--- a/lib/gitlab/database/load_balancing/service_discovery.rb
+++ b/lib/gitlab/database/load_balancing/service_discovery.rb
@@ -151,13 +151,7 @@ module Gitlab
# started just before we added the new hosts it will use an old
# host/connection. While this connection will be checked in and out,
# it won't be explicitly disconnected.
- if Gitlab::Utils.to_boolean(ENV['LOAD_BALANCER_PARALLEL_DISCONNECT'], default: false)
- disconnect_old_hosts(old_hosts)
- else
- old_hosts.each do |host|
- host.disconnect!(timeout: disconnect_timeout)
- end
- end
+ disconnect_old_hosts(old_hosts)
end
# Returns an Array containing:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 643b05d4003..d4277a26485 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -47626,6 +47626,9 @@ msgstr ""
msgid "The selected image is too large."
msgstr ""
+msgid "The selected project is not available"
+msgstr ""
+
msgid "The snippet can be accessed without any authentication."
msgstr ""
diff --git a/spec/frontend/contributors/component/contributors_spec.js b/spec/frontend/contributors/component/contributors_spec.js
index f915b834aff..7d863a8eb78 100644
--- a/spec/frontend/contributors/component/contributors_spec.js
+++ b/spec/frontend/contributors/component/contributors_spec.js
@@ -8,6 +8,7 @@ import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import RefSelector from '~/ref/components/ref_selector.vue';
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
+import { SET_CHART_DATA, SET_LOADING_STATE } from '~/contributors/stores/mutation_types';
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(),
@@ -66,14 +67,14 @@ describe('Contributors charts', () => {
});
it('should display loader whiled loading data', async () => {
- wrapper.vm.$store.state.loading = true;
+ store.commit(SET_LOADING_STATE, true);
await nextTick();
expect(findLoadingIcon().exists()).toBe(true);
});
it('should render charts and a RefSelector when loading completed and there is chart data', async () => {
- wrapper.vm.$store.state.loading = false;
- wrapper.vm.$store.state.chartData = chartData;
+ store.commit(SET_LOADING_STATE, false);
+ store.commit(SET_CHART_DATA, chartData);
await nextTick();
expect(findLoadingIcon().exists()).toBe(false);
@@ -92,8 +93,8 @@ describe('Contributors charts', () => {
});
it('should have a history button with a set href attribute', async () => {
- wrapper.vm.$store.state.loading = false;
- wrapper.vm.$store.state.chartData = chartData;
+ store.commit(SET_LOADING_STATE, false);
+ store.commit(SET_CHART_DATA, chartData);
await nextTick();
const historyButton = findHistoryButton();
@@ -102,8 +103,8 @@ describe('Contributors charts', () => {
});
it('visits a URL when clicking on a branch/tag', async () => {
- wrapper.vm.$store.state.loading = false;
- wrapper.vm.$store.state.chartData = chartData;
+ store.commit(SET_LOADING_STATE, false);
+ store.commit(SET_CHART_DATA, chartData);
await nextTick();
findRefSelector().vm.$emit('input', branch);
diff --git a/spec/frontend/integrations/gitlab_slack_application/components/projects_dropdown_spec.js b/spec/frontend/integrations/gitlab_slack_application/components/projects_dropdown_spec.js
new file mode 100644
index 00000000000..8879a86a578
--- /dev/null
+++ b/spec/frontend/integrations/gitlab_slack_application/components/projects_dropdown_spec.js
@@ -0,0 +1,54 @@
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import ProjectsDropdown from '~/integrations/gitlab_slack_application/components/projects_dropdown.vue';
+
+describe('Slack application projects dropdown', () => {
+ let wrapper;
+
+ const projectsMockData = [
+ {
+ avatar_url: null,
+ id: 1,
+ name: 'Gitlab Smoke Tests',
+ name_with_namespace: 'Toolbox / Gitlab Smoke Tests',
+ },
+ {
+ avatar_url: null,
+ id: 2,
+ name: 'Gitlab Test',
+ name_with_namespace: 'Gitlab Org / Gitlab Test',
+ },
+ {
+ avatar_url: 'foo/bar',
+ id: 3,
+ name: 'Gitlab Shell',
+ name_with_namespace: 'Gitlab Org / Gitlab Shell',
+ },
+ ];
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(ProjectsDropdown, {
+ propsData: {
+ projects: projectsMockData,
+ ...props,
+ },
+ });
+ };
+
+ const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders the listbox with 3 items', () => {
+ expect(findListbox().exists()).toBe(true);
+ expect(findListbox().props('items')).toHaveLength(3);
+ });
+
+ it('should emit project-selected if a project is clicked', () => {
+ findListbox().vm.$emit('select', 1);
+
+ expect(wrapper.emitted('project-selected')).toMatchObject([[projectsMockData[0]]]);
+ });
+});
diff --git a/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb b/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb
index 7197b99fe33..442fa678d4e 100644
--- a/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/service_discovery_spec.rb
@@ -194,7 +194,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery, feature_catego
describe '#replace_hosts' do
before do
- stub_env('LOAD_BALANCER_PARALLEL_DISCONNECT', 'true')
allow(service)
.to receive(:load_balancer)
.and_return(load_balancer)
@@ -257,26 +256,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery, feature_catego
service.replace_hosts([address_foo, address_bar])
end
end
-
- context 'when LOAD_BALANCER_PARALLEL_DISCONNECT is false' do
- before do
- stub_env('LOAD_BALANCER_PARALLEL_DISCONNECT', 'false')
- end
-
- it 'disconnects them sequentially' do
- host = load_balancer.host_list.hosts.first
-
- allow(service)
- .to receive(:disconnect_timeout)
- .and_return(2)
-
- expect(host)
- .to receive(:disconnect!)
- .with(timeout: 2)
-
- service.replace_hosts([address_bar])
- end
- end
end
describe '#addresses_from_dns' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index b36737fc19d..f659f8552aa 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -5956,4 +5956,57 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
it { is_expected.to eq(expected) }
end
end
+
+ describe '#current_patch_id_sha' do
+ let(:merge_request) { build_stubbed(:merge_request) }
+ let(:merge_request_diff) { build_stubbed(:merge_request_diff) }
+ let(:patch_id) { 'ghi789' }
+
+ subject(:current_patch_id_sha) { merge_request.current_patch_id_sha }
+
+ before do
+ allow(merge_request).to receive(:merge_request_diff).and_return(merge_request_diff)
+ allow(merge_request_diff).to receive(:patch_id_sha).and_return(patch_id)
+ end
+
+ it { is_expected.to eq(patch_id) }
+
+ context 'when related merge_request_diff does not have a patch_id_sha' do
+ let(:diff_refs) { instance_double(Gitlab::Diff::DiffRefs, base_sha: base_sha, head_sha: head_sha) }
+ let(:base_sha) { 'abc123' }
+ let(:head_sha) { 'def456' }
+
+ before do
+ allow(merge_request_diff).to receive(:patch_id_sha).and_return(nil)
+ allow(merge_request).to receive(:diff_refs).and_return(diff_refs)
+
+ allow_next_instance_of(Repository) do |repo|
+ allow(repo)
+ .to receive(:get_patch_id)
+ .with(diff_refs.base_sha, diff_refs.head_sha)
+ .and_return(patch_id)
+ end
+ end
+
+ it { is_expected.to eq(patch_id) }
+
+ context 'when base_sha is nil' do
+ let(:base_sha) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when head_sha is nil' do
+ let(:head_sha) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when base_sha and head_sha match' do
+ let(:head_sha) { base_sha }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
end
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index 81fc5661032..e7fe5c19fa3 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -82,39 +82,12 @@ RSpec.describe MergeRequests::ApprovalService, feature_category: :code_review_wo
it 'records a value' do
service.execute(merge_request)
- expect(merge_request.approvals.last.patch_id_sha).not_to be_nil
+ expect(merge_request.approvals.last.patch_id_sha).to eq(merge_request.current_patch_id_sha)
end
- context 'when base_sha is nil' do
+ context 'when MergeRequest#current_patch_id_sha is nil' do
it 'records patch_id_sha as nil' do
- expect_next_instance_of(Gitlab::Diff::DiffRefs) do |diff_ref|
- expect(diff_ref).to receive(:base_sha).at_least(:once).and_return(nil)
- end
-
- service.execute(merge_request)
-
- expect(merge_request.approvals.last.patch_id_sha).to be_nil
- end
- end
-
- context 'when head_sha is nil' do
- it 'records patch_id_sha as nil' do
- expect_next_instance_of(Gitlab::Diff::DiffRefs) do |diff_ref|
- expect(diff_ref).to receive(:head_sha).at_least(:once).and_return(nil)
- end
-
- service.execute(merge_request)
-
- expect(merge_request.approvals.last.patch_id_sha).to be_nil
- end
- end
-
- context 'when base_sha and head_sha match' do
- it 'records patch_id_sha as nil' do
- expect_next_instance_of(Gitlab::Diff::DiffRefs) do |diff_ref|
- expect(diff_ref).to receive(:base_sha).at_least(:once).and_return("abc123")
- expect(diff_ref).to receive(:head_sha).at_least(:once).and_return("abc123")
- end
+ expect(merge_request).to receive(:current_patch_id_sha).and_return(nil)
service.execute(merge_request)
diff --git a/storybook/config/addons/gitlab_api_access/manager.js b/storybook/config/addons/gitlab_api_access/manager.js
index 77d97c76ee2..55e7bcb9c4c 100644
--- a/storybook/config/addons/gitlab_api_access/manager.js
+++ b/storybook/config/addons/gitlab_api_access/manager.js
@@ -41,29 +41,35 @@ const GitLabAPIParametersPanel = () => {
channel.emit(GITLAB_API_ACCESS_UPDATE_EVENT, state);
- return h('div', {}, [
- h(Form.Field, { label: 'GitLab URL' }, [
+ return h(
+ 'div',
+ {},
+ h(
+ Form.Field,
+ { label: 'GitLab URL' },
h(Form.Input, {
type: 'text',
value: state.gitlabURL,
placeholder: 'https://gitlab.com',
onChange: (e) => updateGitLabURL(e),
}),
- ]),
- h(Form.Field, { label: 'GitLab access token' }, [
+ ),
+ h(
+ Form.Field,
+ { label: 'GitLab access token' },
h(Form.Input, {
type: 'password',
value: state.accessToken,
onChange: (e) => updateAccessToken(e),
}),
- ]),
- ]);
+ ),
+ );
};
addons.register(ADDON_ID, () => {
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'GitLab API Access',
- render: ({ active, key }) => h(AddonPanel, { active, key }, [h(GitLabAPIParametersPanel)]),
+ render: ({ active, key }) => h(AddonPanel, { active, key }, h(GitLabAPIParametersPanel)),
});
});
diff --git a/storybook/config/webpack.config.js b/storybook/config/webpack.config.js
index d447211cbd8..cd2e4889300 100644
--- a/storybook/config/webpack.config.js
+++ b/storybook/config/webpack.config.js
@@ -1,10 +1,11 @@
/* eslint-disable no-param-reassign */
-const { statSync } = require('fs');
+const { statSync, existsSync } = require('fs');
const path = require('path');
const glob = require('glob');
const sass = require('sass');
const webpack = require('webpack');
+const { red } = require('chalk');
const IS_EE = require('../../config/helpers/is_ee_env');
const IS_JH = require('../../config/helpers/is_jh_env');
const gitlabWebpackConfig = require('../../config/webpack.config');
@@ -183,5 +184,21 @@ module.exports = function storybookWebpackConfig({ config }) {
// By deleting the alias, imports of this path will resolve as expected.
delete config.resolve.alias['@gitlab/svgs/dist/icons.svg'];
+ // Fail soft if a story requires a fixture, and the fixture file is absent.
+ // Without this, webpack fails at build phase, with a hard to read error.
+ // This rewrite rule pushes the error to be runtime.
+ config.plugins.push(
+ new webpack.NormalModuleReplacementPlugin(/^test_fixtures/, (resource) => {
+ const filename = resource.request.replace(
+ /^test_fixtures/,
+ config.resolve.alias.test_fixtures,
+ );
+ if (!existsSync(filename)) {
+ console.error(red(`\nFixture '${filename}' wasn't found.\n`));
+ resource.request = path.join(ROOT, 'storybook', 'fixture_stub.js');
+ }
+ }),
+ );
+
return config;
};
diff --git a/storybook/fixture_stub.js b/storybook/fixture_stub.js
new file mode 100644
index 00000000000..cd23e038e35
--- /dev/null
+++ b/storybook/fixture_stub.js
@@ -0,0 +1,12 @@
+/**
+ * This file is used as a substitute for absent fixture files. See fixture docs
+ * for how to generate or download them:
+ * https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html#frontend-test-fixtures
+ */
+
+console.error(
+ // eslint-disable-next-line no-restricted-syntax
+ 'Some fixture files could not be found. Generate fixtures or download them. See docs for more: https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html#frontend-test-fixtures',
+);
+
+export default null;