Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
65530963a3
commit
b0c61201a7
43 changed files with 406 additions and 112 deletions
2
Gemfile
2
Gemfile
|
@ -485,7 +485,7 @@ gem 'ssh_data', '~> 1.3'
|
|||
gem 'spamcheck', '~> 0.1.0'
|
||||
|
||||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 15.3.0-rc3'
|
||||
gem 'gitaly', '~> 15.3.0-rc4'
|
||||
|
||||
# KAS GRPC protocol definitions
|
||||
gem 'kas-grpc', '~> 0.0.2'
|
||||
|
|
|
@ -508,7 +508,7 @@ GEM
|
|||
rails (>= 3.2.0)
|
||||
git (1.11.0)
|
||||
rchardet (~> 1.8)
|
||||
gitaly (15.3.0.pre.rc3)
|
||||
gitaly (15.3.0.pre.rc4)
|
||||
grpc (~> 1.0)
|
||||
github-markup (1.7.0)
|
||||
gitlab (4.16.1)
|
||||
|
@ -1574,7 +1574,7 @@ DEPENDENCIES
|
|||
gettext (~> 3.3)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.3)
|
||||
gitaly (~> 15.3.0.pre.rc3)
|
||||
gitaly (~> 15.3.0.pre.rc4)
|
||||
github-markup (~> 1.7.0)
|
||||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 3.5.0)
|
||||
|
|
|
@ -42,7 +42,6 @@ export default {
|
|||
formInputGroupProps() {
|
||||
return {
|
||||
id: this.$options.tokenInputId,
|
||||
class: 'qa-created-access-token',
|
||||
'data-qa-selector': 'created_access_token_field',
|
||||
name: this.$options.tokenInputId,
|
||||
};
|
||||
|
|
|
@ -176,7 +176,11 @@ export default {
|
|||
:placeholder="placeholder"
|
||||
/>
|
||||
</form>
|
||||
<ul v-if="isCreatingNewFile" class="file-templates gl-mt-3 list-inline qa-template-list">
|
||||
<ul
|
||||
v-if="isCreatingNewFile"
|
||||
class="file-templates gl-mt-3 list-inline"
|
||||
data-qa-selector="template_list_content"
|
||||
>
|
||||
<li v-for="(template, index) in templateTypes" :key="index" class="list-inline-item">
|
||||
<gl-button
|
||||
variant="dashed"
|
||||
|
|
|
@ -197,7 +197,7 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="qa-loading-animation gl-mt-6" />
|
||||
<gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-6" />
|
||||
|
||||
<template v-else-if="shouldRenderContent">
|
||||
<div class="build-page" data-testid="job-content">
|
||||
|
|
|
@ -158,7 +158,7 @@ export default {
|
|||
:href="detailsPath"
|
||||
:title="tooltipText"
|
||||
:class="jobClasses"
|
||||
class="js-pipeline-graph-job-link qa-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
|
||||
class="js-pipeline-graph-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
|
||||
data-testid="job-with-link"
|
||||
@click.stop="hideTooltips"
|
||||
@mouseout="hideTooltips"
|
||||
|
|
|
@ -83,9 +83,7 @@ export default {
|
|||
|
||||
<span class="font-weight-bold">{{ __('Pipeline') }}</span>
|
||||
|
||||
<a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
|
||||
>#{{ pipeline.id }}</a
|
||||
>
|
||||
<a :href="pipeline.path" class="js-pipeline-path link-commit">#{{ pipeline.id }}</a>
|
||||
<template v-if="hasRef">
|
||||
{{ __('from') }}
|
||||
<a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a>
|
||||
|
|
|
@ -3,7 +3,6 @@ import { GlLink, GlIcon, GlButton } from '@gitlab/ui';
|
|||
import { __ } from '~/locale';
|
||||
import {
|
||||
issuableIconMap,
|
||||
issuableQaClassMap,
|
||||
linkedIssueTypesMap,
|
||||
linkedIssueTypesTextMap,
|
||||
issuablesBlockHeaderTextMap,
|
||||
|
@ -142,9 +141,6 @@ export default {
|
|||
issuableTypeIcon() {
|
||||
return issuableIconMap[this.issuableType];
|
||||
},
|
||||
qaClass() {
|
||||
return issuableQaClassMap[this.issuableType];
|
||||
},
|
||||
toggleIcon() {
|
||||
return this.isOpen ? 'chevron-lg-up' : 'chevron-lg-down';
|
||||
},
|
||||
|
@ -205,7 +201,6 @@ export default {
|
|||
data-qa-selector="related_issues_plus_button"
|
||||
data-testid="related-issues-plus-button"
|
||||
:aria-label="addIssuableButtonText"
|
||||
:class="qaClass"
|
||||
class="gl-ml-3"
|
||||
@click="addButtonClick"
|
||||
>
|
||||
|
|
|
@ -99,15 +99,6 @@ export const issuableIconMap = {
|
|||
[issuableTypesMap.EPIC]: 'epic',
|
||||
};
|
||||
|
||||
/**
|
||||
* These are used to map issuableType to the correct QA class.
|
||||
* Since these are never used for any display purposes, don't wrap
|
||||
* them inside i18n functions.
|
||||
*/
|
||||
export const issuableQaClassMap = {
|
||||
[issuableTypesMap.EPIC]: 'qa-add-epics-button',
|
||||
};
|
||||
|
||||
export const PathIdSeparator = {
|
||||
Epic: '&',
|
||||
Issue: '#',
|
||||
|
|
|
@ -191,7 +191,7 @@ export default {
|
|||
href: `${this.newBlobPath}/${
|
||||
this.currentPath ? encodeURIComponent(this.currentPath) : ''
|
||||
}`,
|
||||
class: 'qa-new-file-option',
|
||||
'data-qa-selector': 'new_file_menu_item',
|
||||
},
|
||||
text: __('New file'),
|
||||
},
|
||||
|
@ -300,7 +300,11 @@ export default {
|
|||
</router-link>
|
||||
</li>
|
||||
<li v-if="renderAddToTreeDropdown" class="breadcrumb-item">
|
||||
<gl-dropdown toggle-class="add-to-tree qa-add-to-tree gl-ml-2">
|
||||
<gl-dropdown
|
||||
toggle-class="add-to-tree gl-ml-2"
|
||||
data-testid="add-to-tree"
|
||||
data-qa-selector="add_to_tree_dropdown"
|
||||
>
|
||||
<template #button-content>
|
||||
<span class="sr-only">{{ __('Add to tree') }}</span>
|
||||
<gl-icon name="plus" :size="16" class="float-left" />
|
||||
|
|
|
@ -135,7 +135,7 @@ export default {
|
|||
<div
|
||||
class="commit-detail flex-list gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-flex-grow-1 gl-min-w-0"
|
||||
>
|
||||
<div class="commit-content qa-commit-content">
|
||||
<div class="commit-content" data-qa-selector="commit_content">
|
||||
<gl-link
|
||||
v-safe-html:[$options.safeHtmlConfig]="commit.titleHtml"
|
||||
:href="commit.webPath"
|
||||
|
|
|
@ -57,13 +57,19 @@ export default {
|
|||
},
|
||||
cssClass() {
|
||||
const className = this.status.group;
|
||||
return className ? `ci-status ci-${className} qa-status-badge` : 'ci-status qa-status-badge';
|
||||
return className ? `ci-status ci-${className}` : 'ci-status';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<a v-gl-tooltip :href="detailsPath" :class="cssClass" :title="title">
|
||||
<a
|
||||
v-gl-tooltip
|
||||
:href="detailsPath"
|
||||
:class="cssClass"
|
||||
:title="title"
|
||||
data-qa-selector="status_badge_link"
|
||||
>
|
||||
<ci-icon :status="status" :css-classes="iconClasses" />
|
||||
|
||||
<template v-if="showText">
|
||||
|
|
|
@ -47,8 +47,31 @@ module Integrations
|
|||
private
|
||||
|
||||
def notify(message, opts)
|
||||
url = webhook.dup
|
||||
|
||||
key = parse_thread_key(message)
|
||||
url = Gitlab::Utils.add_url_parameters(url, { threadKey: key }) if key
|
||||
|
||||
simple_text = parse_simple_text_message(message)
|
||||
::HangoutsChat::Sender.new(webhook).simple(simple_text)
|
||||
::HangoutsChat::Sender.new(url).simple(simple_text)
|
||||
end
|
||||
|
||||
# Returns an appropriate key for threading messages in google chat
|
||||
def parse_thread_key(message)
|
||||
case message
|
||||
when Integrations::ChatMessage::NoteMessage
|
||||
message.target
|
||||
when Integrations::ChatMessage::IssueMessage
|
||||
"issue #{Issue.reference_prefix}#{message.issue_iid}"
|
||||
when Integrations::ChatMessage::MergeMessage
|
||||
"merge request #{MergeRequest.reference_prefix}#{message.merge_request_iid}"
|
||||
when Integrations::ChatMessage::PushMessage
|
||||
"push #{message.project_name}_#{message.ref}"
|
||||
when Integrations::ChatMessage::PipelineMessage
|
||||
"pipeline #{message.pipeline_id}"
|
||||
when Integrations::ChatMessage::WikiPageMessage
|
||||
"wiki_page #{message.wiki_page_url}"
|
||||
end
|
||||
end
|
||||
|
||||
def parse_simple_text_message(message)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
= _('Your new %{type}') % { type: type }
|
||||
.form-group
|
||||
.input-group
|
||||
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'qa-created-access-token form-control js-select-on-focus', 'aria-describedby' => 'created-token-help-block'
|
||||
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'form-control js-select-on-focus', data: { qa_selector: 'created_access_token_field' }, 'aria-describedby' => 'created-token-help-block'
|
||||
%span.input-group-append
|
||||
= clipboard_button(text: new_token_value, title: _('Copy %{type}') % { type: type }, placement: 'left', class: 'input-group-text btn-default btn-clipboard')
|
||||
%span#created-token-help-block.form-text.text-muted.text-danger
|
||||
|
|
|
@ -660,7 +660,14 @@ The code removed from GitLab during the Gitaly migration project affected these
|
|||
performance workaround for these NFS-based deployments, we re-introduced some of the old Rugged
|
||||
code. This re-introduced code is informally referred to as the "Rugged patches".
|
||||
|
||||
### How it works
|
||||
### Automatic detection
|
||||
|
||||
> Automatic detection for Rugged [disabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95445) in GitLab 15.3.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default automatic detection of whether Rugged should be used (per storage) is not available.
|
||||
To make it available, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named
|
||||
`skip_rugged_auto_detect`.
|
||||
|
||||
The Ruby methods that perform direct Git access are behind
|
||||
[feature flags](../../development/gitaly.md#legacy-rugged-code), disabled by default. It wasn't
|
||||
|
@ -685,12 +692,13 @@ To see if GitLab can access the repository file system directly, we use the foll
|
|||
- GitLab Rails tries to read the metadata file directly. If it exists, and if the UUID's match,
|
||||
assume we have direct access.
|
||||
|
||||
Versions of GitLab 15.3 and later disable direct Git access by default.
|
||||
Direct Git access is:
|
||||
|
||||
For versions of GitLab prior to 15.3, direct Git access is enabled by
|
||||
default in Omnibus GitLab because it fills in the correct repository
|
||||
paths in the GitLab configuration file `config/gitlab.yml`. This
|
||||
satisfies the UUID check.
|
||||
- [Disabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95445) by default in GitLab 15.3 and later for
|
||||
compatibility with [Praefect-generated replica paths](#praefect-generated-replica-paths-gitlab-150-and-later). It
|
||||
can be enabled if Rugged [feature flags](../../development/gitaly.md#legacy-rugged-code) are enabled.
|
||||
- Enabled by default in GitLab 15.2 and earlier because it fills in the correct repository paths in the GitLab
|
||||
configuration file `config/gitlab.yml`. This satisfies the UUID check.
|
||||
|
||||
### Transition to Gitaly Cluster
|
||||
|
||||
|
|
|
@ -95,34 +95,22 @@ We are investigating the use of
|
|||
## Improving NFS performance with GitLab
|
||||
|
||||
NFS performance with GitLab can in some cases be improved with
|
||||
[direct Git access](gitaly/index.md#direct-access-to-git-in-gitlab) using
|
||||
[Rugged](https://github.com/libgit2/rugged).
|
||||
[direct Git access](gitaly/index.md#direct-access-to-git-in-gitlab) using [Rugged](https://github.com/libgit2/rugged).
|
||||
|
||||
Versions of GitLab after 12.2 and prior to 15.3 automatically detect if
|
||||
Rugged can and should be used per storage.
|
||||
Depending on the GitLab version, GitLab [automatically detects](gitaly/index.md#automatic-detection) if Rugged can and should
|
||||
be used per storage.
|
||||
|
||||
NOTE:
|
||||
GitLab 15.3 and later disables this automatic detection. Auto-detection can be enabled via the
|
||||
`skip_rugged_auto_detect` feature flag:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:skip_rugged_auto_detect)
|
||||
```
|
||||
|
||||
In addition, if you previously enabled Rugged using the feature flag and
|
||||
you want to use automatic detection instead, you must unset the feature
|
||||
flag:
|
||||
If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab uses the value explicitly set. If you
|
||||
previously enabled Rugged using the feature flag and you want to use automatic detection instead, you must unset
|
||||
the feature flag:
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:features:unset_rugged
|
||||
```
|
||||
|
||||
If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab uses the value explicitly set.
|
||||
|
||||
From GitLab 12.7, Rugged is only automatically enabled for use with Puma
|
||||
if the [Puma thread count is set to `1`](../install/requirements.md#puma-settings).
|
||||
|
||||
To use Rugged with a Puma thread count of more than `1`, enable Rugged using the [feature flag](../development/gitaly.md#legacy-rugged-code).
|
||||
From GitLab 12.7, Rugged is only automatically enabled for use with Puma if the
|
||||
[Puma thread count is set to `1`](../install/requirements.md#puma-settings). To use Rugged with a Puma thread count of
|
||||
more than `1`, enable Rugged using the [feature flag](../development/gitaly.md#legacy-rugged-code).
|
||||
|
||||
## NFS server
|
||||
|
||||
|
|
|
@ -156,10 +156,8 @@ For deployments where NFS is used to store Git repositories, GitLab uses
|
|||
[direct Git access](../gitaly/index.md#direct-access-to-git-in-gitlab) to improve performance by using
|
||||
[Rugged](https://github.com/libgit2/rugged).
|
||||
|
||||
Rugged usage is automatically enabled if direct Git access
|
||||
[is available](../gitaly/index.md#how-it-works)
|
||||
and Puma is running single threaded, unless it is disabled by a
|
||||
[feature flag](../../development/gitaly.md#legacy-rugged-code).
|
||||
Rugged usage is automatically enabled if direct Git access [is available](../gitaly/index.md#automatic-detection) and
|
||||
Puma is running single threaded, unless it is disabled by a [feature flag](../../development/gitaly.md#legacy-rugged-code).
|
||||
|
||||
MRI Ruby uses a Global VM Lock (GVL). GVL allows MRI Ruby to be multi-threaded, but running at
|
||||
most on a single core.
|
||||
|
|
|
@ -1444,7 +1444,7 @@ response attributes:
|
|||
| Attribute | Type | Description |
|
||||
|:-------------------|:-------|:-------------------------------------------------------------------------------------|
|
||||
| `[].name` | string | Name of the SAML group |
|
||||
| `[].access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
| `[].access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -1458,11 +1458,11 @@ Example response:
|
|||
[
|
||||
{
|
||||
"name": "saml-group-1",
|
||||
"access_level": "Guest"
|
||||
"access_level": 10
|
||||
},
|
||||
{
|
||||
"name": "saml-group-2",
|
||||
"access_level": "Maintainer"
|
||||
"access_level": 40
|
||||
}
|
||||
]
|
||||
```
|
||||
|
@ -1488,7 +1488,7 @@ response attributes:
|
|||
| Attribute | Type | Description |
|
||||
|:---------------|:-------|:-------------------------------------------------------------------------------------|
|
||||
| `name` | string | Name of the SAML group |
|
||||
| `access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
| `access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -1501,7 +1501,7 @@ Example response:
|
|||
```json
|
||||
{
|
||||
"name": "saml-group-1",
|
||||
"access_level": "Guest"
|
||||
"access_level": 10
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1519,7 +1519,7 @@ Supported attributes:
|
|||
|:-------------------|:---------------|:---------|:-------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
|
||||
| `saml_group_name` | string | yes | Name of a SAML group |
|
||||
| `access_level` | string | yes | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
| `access_level` | integer | yes | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
|
||||
If successful, returns [`201`](index.md#status-codes) and the following
|
||||
response attributes:
|
||||
|
@ -1527,7 +1527,7 @@ response attributes:
|
|||
| Attribute | Type | Description |
|
||||
|:---------------|:-------|:-------------------------------------------------------------------------------------|
|
||||
| `name` | string | Name of the SAML group |
|
||||
| `access_level` | string | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
| `access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -1540,7 +1540,7 @@ Example response:
|
|||
```json
|
||||
{
|
||||
"name": "saml-group-1",
|
||||
"access_level": "Guest"
|
||||
"access_level": 10
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -274,6 +274,7 @@ listed in the descriptions of the relevant settings.
|
|||
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
|
||||
| `package_registry_cleanup_policies_worker_capacity` | integer | no | Number of workers assigned to the packages cleanup policies. |
|
||||
| `deactivate_dormant_users` | boolean | no | Enable [automatic deactivation of dormant users](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). |
|
||||
| `deactivate_dormant_users_period` | integer | no | Length of time (in days) after which a user is considered dormant. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.3. |
|
||||
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
|
||||
| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2). |
|
||||
| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both users with the Developer role or Maintainer role can push new commits and force push)_, `1` _(partially protected, users with the Developer role or Maintainer role can push new commits, but cannot force push)_ or `2` _(fully protected, users with the Developer or Maintainer role cannot push new commits, but users with the Developer or Maintainer role can; no one can force push)_ as a parameter. Default is `2`. |
|
||||
|
|
|
@ -121,6 +121,8 @@ On self-managed GitLab, migrating project resources are not available by default
|
|||
- Members ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341886) in GitLab 14.8)
|
||||
- Merge Requests ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.5)
|
||||
- Multiple merge request assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
|
||||
- Migrate Push Rules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.6)
|
||||
- Pull Requests (including external pull requests) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339409) in GitLab 14.5)
|
||||
- Pipeline History ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339412) in GitLab 14.6)
|
||||
|
|
|
@ -43,7 +43,8 @@ configured for personal access tokens.
|
|||
|
||||
## Create a group access token using UI
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
|
||||
|
||||
To create a group access token:
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ Use impersonation tokens to automate authentication as a specific user.
|
|||
|
||||
## Create a personal access token
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI.
|
||||
|
||||
You can create as many personal access tokens as you like.
|
||||
|
||||
1. In the top-right corner, select your avatar.
|
||||
|
|
|
@ -43,7 +43,8 @@ configured for personal access tokens.
|
|||
|
||||
## Create a project access token
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
|
||||
|
||||
To create a project access token:
|
||||
|
||||
|
|
|
@ -9,19 +9,3 @@
|
|||
00_uri: &00_uri
|
||||
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
|
||||
replacement: '\1\2URI_PREFIX\4'
|
||||
07_01_00__gitlab_specific_markdown__footnotes__001:
|
||||
html:
|
||||
static:
|
||||
shared:
|
||||
07_01_00_href: &07_01_00_href
|
||||
- regex: '(href)(=")(.+?)(")'
|
||||
replacement: '\1\2REF\4'
|
||||
07_01_00_id: &07_01_00_id
|
||||
- regex: '(id)(=")(.+?)(")'
|
||||
replacement: '\1\2ID\4'
|
||||
canonical:
|
||||
07_01_00_href: *07_01_00_href
|
||||
07_01_00_id: *07_01_00_id
|
||||
snapshot:
|
||||
07_01_00_href: *07_01_00_href
|
||||
07_01_00_id: *07_01_00_id
|
||||
|
|
|
@ -26,6 +26,19 @@ module Gitlab
|
|||
storage_specific(disk_statistics)
|
||||
end
|
||||
|
||||
def readiness_check
|
||||
request = Gitaly::ReadinessCheckRequest.new(timeout: GitalyClient.medium_timeout)
|
||||
response = GitalyClient.call(@storage, :server_service, :readiness_check, request, timeout: GitalyClient.default_timeout)
|
||||
|
||||
return { success: true } if response.ok_response
|
||||
|
||||
failed_checks = response.failure_response.failed_checks.map do |failed_check|
|
||||
["#{failed_check.name}: #{failed_check.error_message}"]
|
||||
end
|
||||
|
||||
{ success: false, message: failed_checks.join("\n") }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_specific(response)
|
||||
|
|
|
@ -27,17 +27,35 @@ module Gitlab
|
|||
end
|
||||
|
||||
def check(storage_name)
|
||||
serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
|
||||
result = serv.check
|
||||
storage_healthy = healthy(storage_name)
|
||||
unless storage_healthy[:success]
|
||||
return HealthChecks::Result.new(
|
||||
name,
|
||||
storage_healthy[:success],
|
||||
storage_healthy[:message],
|
||||
shard: storage_name
|
||||
)
|
||||
end
|
||||
|
||||
storage_ready = ready(storage_name)
|
||||
HealthChecks::Result.new(
|
||||
name,
|
||||
result[:success],
|
||||
result[:message],
|
||||
storage_ready[:success],
|
||||
storage_ready[:message],
|
||||
shard: storage_name
|
||||
)
|
||||
end
|
||||
|
||||
def healthy(storage_name)
|
||||
serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
|
||||
serv.check
|
||||
end
|
||||
|
||||
def ready(storage_name)
|
||||
serv = Gitlab::GitalyClient::ServerService.new(storage_name)
|
||||
serv.readiness_check
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def metric_prefix
|
||||
|
|
|
@ -18,6 +18,10 @@ module QA
|
|||
element :expiry_date_field
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/access_tokens/_created_container.html.haml' do
|
||||
element :created_access_token_field
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/access_tokens/_form.html.haml' do
|
||||
element :access_token_name_field
|
||||
element :create_token_button
|
||||
|
@ -32,7 +36,7 @@ module QA
|
|||
end
|
||||
|
||||
base.view 'app/assets/javascripts/access_tokens/components/new_access_token_app.vue' do
|
||||
element :created_access_token
|
||||
element :created_access_token_field
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/access_tokens/components/access_token_table_app.vue' do
|
||||
|
@ -53,7 +57,7 @@ module QA
|
|||
end
|
||||
|
||||
def created_access_token
|
||||
find_element(:created_access_token, wait: 30).value
|
||||
find_element(:created_access_token_field, wait: 30).value
|
||||
end
|
||||
|
||||
def fill_expiry_date(date)
|
||||
|
|
|
@ -32,12 +32,12 @@ module QA
|
|||
super
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
|
||||
element :status_badge
|
||||
element :status_badge_link
|
||||
end
|
||||
end
|
||||
|
||||
def status_badge
|
||||
find_element(:status_badge).text
|
||||
find_element(:status_badge_link).text
|
||||
end
|
||||
|
||||
def completed?(timeout: 60)
|
||||
|
|
|
@ -9,7 +9,7 @@ module QA
|
|||
view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do
|
||||
element :file_name_field, required: true
|
||||
element :new_file_modal, required: true
|
||||
element :template_list
|
||||
element :template_list_content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,10 @@ module QA
|
|||
element :commit_message_field
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/repository/components/last_commit.vue' do
|
||||
element :commit_content
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/_commit_message_container.html.haml' do
|
||||
element :commit_message_field
|
||||
end
|
||||
|
|
|
@ -58,8 +58,8 @@ module QA
|
|||
end
|
||||
|
||||
view 'app/assets/javascripts/repository/components/breadcrumbs.vue' do
|
||||
element :add_to_tree
|
||||
element :new_file_option
|
||||
element :add_to_tree_dropdown
|
||||
element :new_file_menu_item
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do
|
||||
|
@ -90,8 +90,8 @@ module QA
|
|||
end
|
||||
|
||||
def create_new_file!
|
||||
click_element :add_to_tree
|
||||
click_element :new_file_option
|
||||
click_element :add_to_tree_dropdown
|
||||
click_element :new_file_menu_item
|
||||
end
|
||||
|
||||
def fork_project
|
||||
|
|
|
@ -163,7 +163,7 @@ module QA
|
|||
def create_new_file_from_template(file_name, template)
|
||||
click_element(:new_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
|
||||
|
||||
within_element(:template_list) do
|
||||
within_element(:template_list_content) do
|
||||
click_on file_name
|
||||
rescue Capybara::ElementNotFound
|
||||
raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
|
||||
|
|
|
@ -39,7 +39,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do
|
|||
|
||||
# The dropdown above the tree
|
||||
page.within('.repo-breadcrumb') do
|
||||
find('.qa-add-to-tree').click # rubocop:disable QA/SelectorUsage
|
||||
find('[data-testid="add-to-tree"]').click
|
||||
|
||||
aggregate_failures 'dropdown links above the repo tree' do
|
||||
expect(page).to have_link('New file')
|
||||
|
@ -71,7 +71,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do
|
|||
find_new_menu_toggle.click
|
||||
end
|
||||
|
||||
expect(page).not_to have_selector('.qa-add-to-tree') # rubocop:disable QA/SelectorUsage
|
||||
expect(page).not_to have_selector('[data-testid="add-to-tree"]')
|
||||
|
||||
expect(page).not_to have_link('Web IDE')
|
||||
end
|
||||
|
|
|
@ -78,7 +78,6 @@ describe('~/access_tokens/components/new_access_token_app', () => {
|
|||
.findByLabelText(sprintf(__('Your new %{accessTokenType}'), { accessTokenType }))
|
||||
.attributes();
|
||||
expect(inputAttributes).toMatchObject({
|
||||
class: expect.stringContaining('qa-created-access-token'),
|
||||
'data-qa-selector': 'created_access_token_field',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
|
|||
class="commit-detail flex-list gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-flex-grow-1 gl-min-w-0"
|
||||
>
|
||||
<div
|
||||
class="commit-content qa-commit-content"
|
||||
class="commit-content"
|
||||
data-qa-selector="commit_content"
|
||||
>
|
||||
<gl-link-stub
|
||||
class="commit-row-message item-title"
|
||||
|
|
42
spec/lib/gitlab/gitaly_client/server_service_spec.rb
Normal file
42
spec/lib/gitlab/gitaly_client/server_service_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GitalyClient::ServerService do
|
||||
let(:storage) { 'default' }
|
||||
|
||||
describe '#readiness_check' do
|
||||
before do
|
||||
::Gitlab::GitalyClient.clear_stubs!
|
||||
end
|
||||
|
||||
let(:request) do
|
||||
Gitaly::ReadinessCheckRequest.new(timeout: 30)
|
||||
end
|
||||
|
||||
subject(:readiness_check) { described_class.new(storage).readiness_check }
|
||||
|
||||
it 'returns a positive success if no failures happened' do
|
||||
expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
|
||||
response = Gitaly::ReadinessCheckResponse.new(ok_response: Gitaly::ReadinessCheckResponse::Ok.new)
|
||||
expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
|
||||
end
|
||||
|
||||
expect(readiness_check[:success]).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns a negative success and a compiled message if at least one failure happened' do
|
||||
failure1 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '1', error_message: 'msg 1')
|
||||
failure2 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '2', error_message: 'msg 2')
|
||||
failures = Gitaly::ReadinessCheckResponse::Failure.new(failed_checks: [failure1, failure2])
|
||||
response = Gitaly::ReadinessCheckResponse.new(failure_response: failures)
|
||||
|
||||
expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
|
||||
expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
|
||||
end
|
||||
|
||||
expect(readiness_check[:success]).to eq(false)
|
||||
expect(readiness_check[:message]).to eq("1: msg 1\n2: msg 2")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,20 +14,36 @@ RSpec.describe Gitlab::HealthChecks::GitalyCheck do
|
|||
subject { described_class.readiness }
|
||||
|
||||
before do
|
||||
expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
|
||||
expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(healthy_check)
|
||||
end
|
||||
|
||||
context 'Gitaly server is up' do
|
||||
let(:gitaly_check) { double(check: { success: true }) }
|
||||
before do
|
||||
expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
|
||||
end
|
||||
|
||||
let(:healthy_check) { double(check: { success: true }) }
|
||||
let(:ready_check) { double(readiness_check: { success: true }) }
|
||||
|
||||
it { is_expected.to eq([result_class.new('gitaly_check', true, nil, shard: 'default')]) }
|
||||
end
|
||||
|
||||
context 'Gitaly server is down' do
|
||||
let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
|
||||
let(:healthy_check) { double(check: { success: false, message: 'Connection refused' }) }
|
||||
|
||||
it { is_expected.to eq([result_class.new('gitaly_check', false, 'Connection refused', shard: 'default')]) }
|
||||
end
|
||||
|
||||
context 'Gitaly server is not ready' do
|
||||
before do
|
||||
expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
|
||||
end
|
||||
|
||||
let(:healthy_check) { double(check: { success: true }) }
|
||||
let(:ready_check) { double(readiness_check: { success: false, message: 'Clock is out of sync' }) }
|
||||
|
||||
it { is_expected.to match_array([result_class.new('gitaly_check', false, 'Clock is out of sync', shard: 'default')]) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#metrics' do
|
||||
|
|
|
@ -24,6 +24,10 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
|
|||
end
|
||||
|
||||
it 'responds with readiness checks data' do
|
||||
expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
|
||||
expect(service).to receive(:readiness_check).and_return({ success: true })
|
||||
end
|
||||
|
||||
expect(subject.http_status).to eq(200)
|
||||
|
||||
expect(subject.json[:status]).to eq('ok')
|
||||
|
|
|
@ -12,4 +12,175 @@ RSpec.describe Integrations::HangoutsChat do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
let(:chat_integration) { described_class.new }
|
||||
let(:webhook_url) { 'https://example.gitlab.com/' }
|
||||
let(:webhook_url_regex) { /\A#{webhook_url}.*/ }
|
||||
|
||||
describe "#execute" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :repository, :wiki_repo) }
|
||||
|
||||
before do
|
||||
allow(chat_integration).to receive_messages(
|
||||
project: project,
|
||||
project_id: project.id,
|
||||
webhook: webhook_url
|
||||
)
|
||||
|
||||
WebMock.stub_request(:post, webhook_url_regex)
|
||||
end
|
||||
|
||||
context 'with push events' do
|
||||
let(:push_sample_data) do
|
||||
Gitlab::DataBuilder::Push.build_sample(project, user)
|
||||
end
|
||||
|
||||
it "adds thread key for push events" do
|
||||
expect(chat_integration.execute(push_sample_data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /push .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with issue events' do
|
||||
let(:issues_sample_data) { create(:issue).to_hook_data(user) }
|
||||
|
||||
it "adds thread key for issue events" do
|
||||
expect(chat_integration.execute(issues_sample_data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /issue .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with merge events' do
|
||||
let(:merge_sample_data) { create(:merge_request).to_hook_data(user) }
|
||||
|
||||
it "adds thread key for merge events" do
|
||||
expect(chat_integration.execute(merge_sample_data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /merge request .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with wiki page events' do
|
||||
let(:wiki_page_sample_data) do
|
||||
Gitlab::DataBuilder::WikiPage.build(create(:wiki_page, message: 'foo'), user, 'create')
|
||||
end
|
||||
|
||||
it "adds thread key for wiki page events" do
|
||||
expect(chat_integration.execute(wiki_page_sample_data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /wiki_page .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'with pipeline events' do
|
||||
let(:pipeline) do
|
||||
create(:ci_pipeline, :failed, project: project, sha: project.commit.sha, ref: project.default_branch)
|
||||
end
|
||||
|
||||
let(:pipeline_sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
|
||||
|
||||
it "adds thread key for pipeline events" do
|
||||
expect(chat_integration.execute(pipeline_sample_data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /pipeline .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Note events" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :repository, creator: user) }
|
||||
|
||||
before do
|
||||
allow(chat_integration).to receive_messages(
|
||||
project: project,
|
||||
project_id: project.id,
|
||||
webhook: webhook_url
|
||||
)
|
||||
|
||||
WebMock.stub_request(:post, webhook_url_regex)
|
||||
end
|
||||
|
||||
context 'when commit comment event executed' do
|
||||
let(:commit_note) do
|
||||
create(:note_on_commit, author: user,
|
||||
project: project,
|
||||
commit_id: project.repository.commit.id,
|
||||
note: 'a comment on a commit')
|
||||
end
|
||||
|
||||
it "adds thread key" do
|
||||
data = Gitlab::DataBuilder::Note.build(commit_note, user)
|
||||
|
||||
expect(chat_integration.execute(data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /commit .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge request comment event executed' do
|
||||
let(:merge_request_note) do
|
||||
create(:note_on_merge_request, project: project,
|
||||
note: "merge request note")
|
||||
end
|
||||
|
||||
it "adds thread key" do
|
||||
data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
|
||||
|
||||
expect(chat_integration.execute(data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /merge request .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue comment event executed' do
|
||||
let(:issue_note) do
|
||||
create(:note_on_issue, project: project, note: "issue note")
|
||||
end
|
||||
|
||||
it "adds thread key" do
|
||||
data = Gitlab::DataBuilder::Note.build(issue_note, user)
|
||||
|
||||
expect(chat_integration.execute(data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /issue .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
|
||||
context 'when snippet comment event executed' do
|
||||
let(:snippet_note) do
|
||||
create(:note_on_project_snippet, project: project,
|
||||
note: "snippet note")
|
||||
end
|
||||
|
||||
it "adds thread key" do
|
||||
data = Gitlab::DataBuilder::Note.build(snippet_note, user)
|
||||
|
||||
expect(chat_integration.execute(data)).to be(true)
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url)
|
||||
.with(query: hash_including({ "threadKey" => /snippet .*?/ }))
|
||||
.once
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -127,6 +127,10 @@ RSpec.describe HealthController do
|
|||
end
|
||||
|
||||
it 'responds with readiness checks data' do
|
||||
expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
|
||||
expect(service).to receive(:readiness_check).and_return({ success: true })
|
||||
end
|
||||
|
||||
subject
|
||||
|
||||
expect(json_response['db_check']).to contain_exactly({ 'status' => 'ok' })
|
||||
|
|
|
@ -10,6 +10,14 @@ RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_speci
|
|||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:api_url) { api('/markdown', user) }
|
||||
|
||||
before do
|
||||
# Set 'GITLAB_TEST_FOOTNOTE_ID' in order to override random number generation in
|
||||
# Banzai::Filter::FootnoteFilter#random_number, and thus avoid the need to
|
||||
# perform normalization on the value. See:
|
||||
# https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
|
||||
stub_env('GITLAB_TEST_FOOTNOTE_ID', 42)
|
||||
end
|
||||
|
||||
markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
|
||||
yaml = File.read("#{glfm_specification_dir}/example_snapshots/#{file_name}")
|
||||
YAML.safe_load(yaml, symbolize_names: true, aliases: true)
|
||||
|
|
|
@ -36,6 +36,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
|
|||
let_it_be_with_reload(:project) { create(:project, :repository) }
|
||||
|
||||
let(:webhook_url) { "https://example.gitlab.com/" }
|
||||
let(:webhook_url_regex) { /\A#{webhook_url}.*/ }
|
||||
|
||||
before do
|
||||
allow(subject).to receive_messages(
|
||||
|
@ -45,7 +46,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
|
|||
webhook: webhook_url
|
||||
)
|
||||
|
||||
WebMock.stub_request(:post, webhook_url)
|
||||
WebMock.stub_request(:post, webhook_url_regex)
|
||||
end
|
||||
|
||||
shared_examples "triggered #{integration_name} integration" do |branches_to_be_notified: nil|
|
||||
|
@ -57,7 +58,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
|
|||
result = subject.execute(sample_data)
|
||||
|
||||
expect(result).to be(true)
|
||||
expect(WebMock).to have_requested(:post, webhook_url).once.with { |req|
|
||||
expect(WebMock).to have_requested(:post, webhook_url_regex).once.with { |req|
|
||||
json_body = Gitlab::Json.parse(req.body).with_indifferent_access
|
||||
expect(json_body).to include(payload)
|
||||
}
|
||||
|
@ -73,7 +74,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
|
|||
result = subject.execute(sample_data)
|
||||
|
||||
expect(result).to be_falsy
|
||||
expect(WebMock).not_to have_requested(:post, webhook_url)
|
||||
expect(WebMock).not_to have_requested(:post, webhook_url_regex)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ RSpec.describe RepositoryCheck::DispatchWorker do
|
|||
end
|
||||
|
||||
it 'dispatches work to RepositoryCheck::BatchWorker' do
|
||||
expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
|
||||
expect(service).to receive(:readiness_check).and_return({ success: true })
|
||||
end
|
||||
|
||||
expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once)
|
||||
|
||||
subject.perform
|
||||
|
|
Loading…
Reference in a new issue