Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0698388e65
commit
b941629bbf
32 changed files with 336 additions and 52 deletions
|
@ -1 +1 @@
|
||||||
a75309cec88ed34f594a4f6514bb0bb2aef7fcd5
|
fdd1fe70085c1a20b10553680d88a967a4cfbfae
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<script>
|
||||||
|
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GlLink,
|
||||||
|
GlSprintf,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['ancestorHelperPath', 'hasAncestorClusters']),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="hasAncestorClusters" class="bs-callout bs-callout-info">
|
||||||
|
<p>
|
||||||
|
<gl-sprintf
|
||||||
|
:message="
|
||||||
|
s__(
|
||||||
|
'ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #link="{ content }">
|
||||||
|
<gl-link :href="ancestorHelperPath">
|
||||||
|
<strong>{{ content }}</strong>
|
||||||
|
</gl-link>
|
||||||
|
</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -9,6 +9,7 @@ import {
|
||||||
GlSprintf,
|
GlSprintf,
|
||||||
GlTable,
|
GlTable,
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
|
import AncestorNotice from './ancestor_notice.vue';
|
||||||
import tooltip from '~/vue_shared/directives/tooltip';
|
import tooltip from '~/vue_shared/directives/tooltip';
|
||||||
import { CLUSTER_TYPES, STATUSES } from '../constants';
|
import { CLUSTER_TYPES, STATUSES } from '../constants';
|
||||||
import { __, sprintf } from '~/locale';
|
import { __, sprintf } from '~/locale';
|
||||||
|
@ -17,6 +18,7 @@ export default {
|
||||||
nodeMemoryText: __('%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)'),
|
nodeMemoryText: __('%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)'),
|
||||||
nodeCpuText: __('%{totalCpu} (%{freeSpacePercentage}%{percentSymbol} free)'),
|
nodeCpuText: __('%{totalCpu} (%{freeSpacePercentage}%{percentSymbol} free)'),
|
||||||
components: {
|
components: {
|
||||||
|
AncestorNotice,
|
||||||
GlBadge,
|
GlBadge,
|
||||||
GlLink,
|
GlLink,
|
||||||
GlLoadingIcon,
|
GlLoadingIcon,
|
||||||
|
@ -195,6 +197,8 @@ export default {
|
||||||
<gl-loading-icon v-if="loadingClusters" size="md" class="gl-mt-3" />
|
<gl-loading-icon v-if="loadingClusters" size="md" class="gl-mt-3" />
|
||||||
|
|
||||||
<section v-else>
|
<section v-else>
|
||||||
|
<ancestor-notice />
|
||||||
|
|
||||||
<gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
|
<gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
|
||||||
<template #cell(name)="{ item }">
|
<template #cell(name)="{ item }">
|
||||||
<div :class="[contentAlignClasses, 'js-status']">
|
<div :class="[contentAlignClasses, 'js-status']">
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default (initialState = {}) => ({
|
export default (initialState = {}) => ({
|
||||||
|
ancestorHelperPath: initialState.ancestorHelpPath,
|
||||||
endpoint: initialState.endpoint,
|
endpoint: initialState.endpoint,
|
||||||
hasAncestorClusters: false,
|
hasAncestorClusters: false,
|
||||||
clusters: [],
|
clusters: [],
|
||||||
|
|
|
@ -13,7 +13,11 @@ module Types
|
||||||
field :jira_email, GraphQL::STRING_TYPE, null: true,
|
field :jira_email, GraphQL::STRING_TYPE, null: true,
|
||||||
description: 'Email of the Jira user, returned only for users with public emails'
|
description: 'Email of the Jira user, returned only for users with public emails'
|
||||||
field :gitlab_id, GraphQL::INT_TYPE, null: true,
|
field :gitlab_id, GraphQL::INT_TYPE, null: true,
|
||||||
description: 'Id of the matched GitLab user'
|
description: 'ID of the matched GitLab user'
|
||||||
|
field :gitlab_username, GraphQL::STRING_TYPE, null: true,
|
||||||
|
description: 'Username of the matched GitLab user'
|
||||||
|
field :gitlab_name, GraphQL::STRING_TYPE, null: true,
|
||||||
|
description: 'Name of the matched GitLab user'
|
||||||
end
|
end
|
||||||
# rubocop: enable Graphql/AuthorizeTypes
|
# rubocop: enable Graphql/AuthorizeTypes
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,7 @@ module ClustersHelper
|
||||||
|
|
||||||
def js_clusters_list_data(path = nil)
|
def js_clusters_list_data(path = nil)
|
||||||
{
|
{
|
||||||
|
ancestor_help_path: help_page_path('user/group/clusters/index', anchor: 'cluster-precedence'),
|
||||||
endpoint: path,
|
endpoint: path,
|
||||||
img_tags: {
|
img_tags: {
|
||||||
aws: { path: image_path('illustrations/logos/amazon_eks.svg'), text: s_('ClusterIntegration|Amazon EKS') },
|
aws: { path: image_path('illustrations/logos/amazon_eks.svg'), text: s_('ClusterIntegration|Amazon EKS') },
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Ci
|
||||||
METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'.freeze
|
METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'.freeze
|
||||||
DEFAULT_METRICS_SHARD = 'default'.freeze
|
DEFAULT_METRICS_SHARD = 'default'.freeze
|
||||||
|
|
||||||
Result = Struct.new(:build, :valid?)
|
Result = Struct.new(:build, :build_json, :valid?)
|
||||||
|
|
||||||
def initialize(runner)
|
def initialize(runner)
|
||||||
@runner = runner
|
@runner = runner
|
||||||
|
@ -59,7 +59,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
register_failure
|
register_failure
|
||||||
Result.new(nil, valid)
|
Result.new(nil, nil, valid)
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ module Ci
|
||||||
# In case when 2 runners try to assign the same build, second runner will be declined
|
# In case when 2 runners try to assign the same build, second runner will be declined
|
||||||
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
|
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
|
||||||
if assign_runner!(build, params)
|
if assign_runner!(build, params)
|
||||||
Result.new(build, true)
|
present_build!(build)
|
||||||
end
|
end
|
||||||
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
|
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
|
||||||
# We are looping to find another build that is not conflicting
|
# We are looping to find another build that is not conflicting
|
||||||
|
@ -83,8 +83,10 @@ module Ci
|
||||||
# In case we hit the concurrency-access lock,
|
# In case we hit the concurrency-access lock,
|
||||||
# we still have to return 409 in the end,
|
# we still have to return 409 in the end,
|
||||||
# to make sure that this is properly handled by runner.
|
# to make sure that this is properly handled by runner.
|
||||||
Result.new(nil, false)
|
Result.new(nil, nil, false)
|
||||||
rescue => ex
|
rescue => ex
|
||||||
|
# If an error (e.g. GRPC::DeadlineExceeded) occurred constructing
|
||||||
|
# the result, consider this as a failure to be retried.
|
||||||
scheduler_failure!(build)
|
scheduler_failure!(build)
|
||||||
track_exception_for_build(ex, build)
|
track_exception_for_build(ex, build)
|
||||||
|
|
||||||
|
@ -92,6 +94,15 @@ module Ci
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Force variables evaluation to occur now
|
||||||
|
def present_build!(build)
|
||||||
|
# We need to use the presenter here because Gitaly calls in the presenter
|
||||||
|
# may fail, and we need to ensure the response has been generated.
|
||||||
|
presented_build = ::Ci::BuildRunnerPresenter.new(build) # rubocop:disable CodeReuse/Presenter
|
||||||
|
build_json = ::API::Entities::JobRequest::Response.new(presented_build).to_json
|
||||||
|
Result.new(build, build_json, true)
|
||||||
|
end
|
||||||
|
|
||||||
def assign_runner!(build, params)
|
def assign_runner!(build, params)
|
||||||
build.runner_id = runner.id
|
build.runner_id = runner.id
|
||||||
build.runner_session_attributes = params[:session] if params[:session].present?
|
build.runner_session_attributes = params[:session] if params[:session].present?
|
||||||
|
|
|
@ -14,9 +14,8 @@ module JiraImport
|
||||||
{
|
{
|
||||||
jira_account_id: jira_user['accountId'],
|
jira_account_id: jira_user['accountId'],
|
||||||
jira_display_name: jira_user['displayName'],
|
jira_display_name: jira_user['displayName'],
|
||||||
jira_email: jira_user['emailAddress'],
|
jira_email: jira_user['emailAddress']
|
||||||
gitlab_id: match_user(jira_user)
|
}.merge(match_user(jira_user))
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,7 +24,7 @@ module JiraImport
|
||||||
# TODO: Matching user by email and displayName will be done as the part
|
# TODO: Matching user by email and displayName will be done as the part
|
||||||
# of follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/219023
|
# of follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/219023
|
||||||
def match_user(jira_user)
|
def match_user(jira_user)
|
||||||
nil
|
{ gitlab_id: nil, gitlab_username: nil, gitlab_name: nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,15 +12,14 @@
|
||||||
= s_('ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project')
|
= s_('ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project')
|
||||||
= render 'clusters/clusters/buttons'
|
= render 'clusters/clusters/buttons'
|
||||||
|
|
||||||
- if @has_ancestor_clusters
|
|
||||||
.bs-callout.bs-callout-info
|
|
||||||
= s_('ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.')
|
|
||||||
%strong
|
|
||||||
= link_to _('More information'), help_page_path('user/group/clusters/index', anchor: 'cluster-precedence')
|
|
||||||
|
|
||||||
- if Feature.enabled?(:clusters_list_redesign)
|
- if Feature.enabled?(:clusters_list_redesign)
|
||||||
#js-clusters-list-app{ data: js_clusters_list_data(clusterable.index_path(format: :json)) }
|
#js-clusters-list-app{ data: js_clusters_list_data(clusterable.index_path(format: :json)) }
|
||||||
- else
|
- else
|
||||||
|
- if @has_ancestor_clusters
|
||||||
|
.bs-callout.bs-callout-info
|
||||||
|
= s_('ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.')
|
||||||
|
%strong
|
||||||
|
= link_to _('More information'), help_page_path('user/group/clusters/index', anchor: 'cluster-precedence')
|
||||||
.clusters-table.js-clusters-list
|
.clusters-table.js-clusters-list
|
||||||
.gl-responsive-table-row.table-row-header{ role: "row" }
|
.gl-responsive-table-row.table-row-header{ role: "row" }
|
||||||
.table-section.section-60{ role: "rowheader" }
|
.table-section.section-60{ role: "rowheader" }
|
||||||
|
|
|
@ -60,8 +60,6 @@ module Reenqueuer
|
||||||
5.seconds
|
5.seconds
|
||||||
end
|
end
|
||||||
|
|
||||||
# We intend to get rid of sleep:
|
|
||||||
# https://gitlab.com/gitlab-org/gitlab/issues/121697
|
|
||||||
module ReenqueuerSleeper
|
module ReenqueuerSleeper
|
||||||
# The block will run, and then sleep until the minimum duration. Returns the
|
# The block will run, and then sleep until the minimum duration. Returns the
|
||||||
# block's return value.
|
# block's return value.
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add GitLab username and name to the import users from Jira mutation response
|
||||||
|
merge_request: 35542
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Change PrometheusMetrics identifier index
|
||||||
|
merge_request: 35912
|
||||||
|
author:
|
||||||
|
type: fixed
|
5
changelogs/unreleased/sh-requeue-failed-job-register.yml
Normal file
5
changelogs/unreleased/sh-requeue-failed-job-register.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fail jobs that fail to render registration response
|
||||||
|
merge_request: 36274
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,22 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ChangePrometheusMetricsIdentifierIndex < ActiveRecord::Migration[6.0]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
NEW_INDEX = :index_prometheus_metrics_on_identifier_and_null_project
|
||||||
|
OLD_INDEX = :index_prometheus_metrics_on_identifier
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_concurrent_index :prometheus_metrics, :identifier, name: NEW_INDEX, unique: true, where: 'project_id IS NULL'
|
||||||
|
remove_concurrent_index_by_name :prometheus_metrics, OLD_INDEX
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_index :prometheus_metrics, :identifier, name: OLD_INDEX, unique: true
|
||||||
|
remove_concurrent_index_by_name :prometheus_metrics, NEW_INDEX
|
||||||
|
end
|
||||||
|
end
|
|
@ -20025,7 +20025,7 @@ CREATE INDEX index_prometheus_metrics_on_common ON public.prometheus_metrics USI
|
||||||
|
|
||||||
CREATE INDEX index_prometheus_metrics_on_group ON public.prometheus_metrics USING btree ("group");
|
CREATE INDEX index_prometheus_metrics_on_group ON public.prometheus_metrics USING btree ("group");
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier ON public.prometheus_metrics USING btree (identifier);
|
CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier_and_null_project ON public.prometheus_metrics USING btree (identifier) WHERE (project_id IS NULL);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier_and_project_id ON public.prometheus_metrics USING btree (identifier, project_id);
|
CREATE UNIQUE INDEX index_prometheus_metrics_on_identifier_and_project_id ON public.prometheus_metrics USING btree (identifier, project_id);
|
||||||
|
|
||||||
|
@ -23718,6 +23718,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
||||||
20200701093859
|
20200701093859
|
||||||
20200701205710
|
20200701205710
|
||||||
20200702123805
|
20200702123805
|
||||||
|
20200702201039
|
||||||
20200703064117
|
20200703064117
|
||||||
20200703121557
|
20200703121557
|
||||||
20200703154822
|
20200703154822
|
||||||
|
|
|
@ -35,7 +35,8 @@ The availability objectives for Gitaly clusters are:
|
||||||
Writes are replicated asynchronously. Any writes that have not been replicated
|
Writes are replicated asynchronously. Any writes that have not been replicated
|
||||||
to the newly promoted primary are lost.
|
to the newly promoted primary are lost.
|
||||||
|
|
||||||
[Strong consistency](#strong-consistency) can be used to improve this to "no loss".
|
[Strong consistency](#strong-consistency) can be used to avoid loss in some
|
||||||
|
circumstances.
|
||||||
|
|
||||||
- **Recovery Time Objective (RTO):** Less than 10 seconds.
|
- **Recovery Time Objective (RTO):** Less than 10 seconds.
|
||||||
|
|
||||||
|
@ -886,8 +887,8 @@ after the write to the primary Gitaly node has happened.
|
||||||
Praefect can instead provide strong consistency by creating a transaction and writing
|
Praefect can instead provide strong consistency by creating a transaction and writing
|
||||||
changes to all Gitaly nodes at once. Strong consistency is currently in
|
changes to all Gitaly nodes at once. Strong consistency is currently in
|
||||||
[alpha](https://about.gitlab.com/handbook/product/#alpha-beta-ga) and not enabled by
|
[alpha](https://about.gitlab.com/handbook/product/#alpha-beta-ga) and not enabled by
|
||||||
default. For more information, see the
|
default. If enabled, transactions are only available for a subset of RPCs. For more
|
||||||
[strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189).
|
information, see the [strong consistency epic](https://gitlab.com/groups/gitlab-org/-/epics/1189).
|
||||||
|
|
||||||
To enable strong consistency:
|
To enable strong consistency:
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,49 @@ For `<project>.git` you'll need to
|
||||||
[translate your project name into the hashed storage format](repository_storage_types.md#translating-hashed-storage-paths)
|
[translate your project name into the hashed storage format](repository_storage_types.md#translating-hashed-storage-paths)
|
||||||
that GitLab uses.
|
that GitLab uses.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
The following set of environment variables are available to server hooks.
|
||||||
|
|
||||||
|
### GitLab Environment Variables
|
||||||
|
|
||||||
|
| Envirnment Variable | purpose |
|
||||||
|
|---------------------|---------------------------------------------------------|
|
||||||
|
| GL_ID | GitLab identifier eg: user-2234 that initiated the push |
|
||||||
|
| GL_PROJECT_PATH (available starting 13.2) | GitLab project path |
|
||||||
|
| GL_PROTOCOL (available starting 13.2) | Protocol used with push |
|
||||||
|
| GL_REPOSITORY | project-<id> where id of the project |
|
||||||
|
| GL_USERNAME | GitLab username that initiated the push |
|
||||||
|
|
||||||
|
Pre-receive and post-receive server hooks can also access the following Git environment variables.
|
||||||
|
|
||||||
|
| Environment variable | Description |
|
||||||
|
|:-----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `GIT_ALTERNATE_OBJECT_DIRECTORIES` | Alternate object directories in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
||||||
|
| `GIT_OBJECT_DIRECTORY` | GitLab project path in the quarantine environment. See [Git `receive-pack` documentation](https://git-scm.com/docs/git-receive-pack#_quarantine_environment). |
|
||||||
|
| `GIT_PUSH_OPTION_COUNT` | Number of push options. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
||||||
|
| `GIT_PUSH_OPTION_<i>` | Value of push options where `i` is from `0` to `GIT_PUSH_OPTION_COUNT - 1`. See [Git `pre-receive` documentation](https://git-scm.com/docs/githooks#pre-receive). |
|
||||||
|
|
||||||
|
NOTE: **Note:**
|
||||||
|
While other environment variables can be passed to server hooks, your application
|
||||||
|
should not rely on them as they can change.
|
||||||
|
|
||||||
|
## Transition to Go
|
||||||
|
|
||||||
|
> Introduced in GitLab 13.2 using feature flags.
|
||||||
|
|
||||||
|
The following server hooks have been reimplemented in Go:
|
||||||
|
|
||||||
|
- `pre-receive`, with the Go implementation used by default. To use the Ruby
|
||||||
|
implementation instead, [disable](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies)
|
||||||
|
the `:gitaly_go_preceive_hook` feature flag.
|
||||||
|
- `update`, with the Go implementation used by default. To use the Ruby implementation
|
||||||
|
instead, [disable](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies)
|
||||||
|
the `:gitaly_go_update_hook` feature flag.
|
||||||
|
- `post-receive`, however the Ruby implementation is used by default. To use the Go
|
||||||
|
implementation instead, [enabled](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies)
|
||||||
|
the `:gitaly_go_postreceive_hook` feature flag.
|
||||||
|
|
||||||
## Custom error messages
|
## Custom error messages
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5073) in GitLab 8.10.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5073) in GitLab 8.10.
|
||||||
|
|
|
@ -423,7 +423,7 @@ Status: 200 OK
|
||||||
```
|
```
|
||||||
|
|
||||||
CAUTION: **Deprecation:**
|
CAUTION: **Deprecation:**
|
||||||
The `Links` Header will be removed in GitLab 14.0 to be aligned with the [W3C specification](https://www.w3.org/wiki/LinkHeader)
|
The `Links` Header will be removed in GitLab 14.0 to be aligned with the [W3C `Link` specification](https://www.w3.org/wiki/LinkHeader)
|
||||||
|
|
||||||
The link to the next page contains an additional filter `id_after=42` which excludes records we have retrieved already.
|
The link to the next page contains an additional filter `id_after=42` which excludes records we have retrieved already.
|
||||||
Note the type of filter depends on the `order_by` option used and we may have more than one additional filter.
|
Note the type of filter depends on the `order_by` option used and we may have more than one additional filter.
|
||||||
|
|
|
@ -6656,10 +6656,20 @@ type JiraService implements Service {
|
||||||
|
|
||||||
type JiraUser {
|
type JiraUser {
|
||||||
"""
|
"""
|
||||||
Id of the matched GitLab user
|
ID of the matched GitLab user
|
||||||
"""
|
"""
|
||||||
gitlabId: Int
|
gitlabId: Int
|
||||||
|
|
||||||
|
"""
|
||||||
|
Name of the matched GitLab user
|
||||||
|
"""
|
||||||
|
gitlabName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Username of the matched GitLab user
|
||||||
|
"""
|
||||||
|
gitlabUsername: String
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Account id of the Jira user
|
Account id of the Jira user
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18439,7 +18439,7 @@
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "gitlabId",
|
"name": "gitlabId",
|
||||||
"description": "Id of the matched GitLab user",
|
"description": "ID of the matched GitLab user",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -18451,6 +18451,34 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "gitlabName",
|
||||||
|
"description": "Name of the matched GitLab user",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gitlabUsername",
|
||||||
|
"description": "Username of the matched GitLab user",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "jiraAccountId",
|
"name": "jiraAccountId",
|
||||||
"description": "Account id of the Jira user",
|
"description": "Account id of the Jira user",
|
||||||
|
|
|
@ -1007,7 +1007,9 @@ Autogenerated return type of JiraImportUsers
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| --- | ---- | ---------- |
|
| --- | ---- | ---------- |
|
||||||
| `gitlabId` | Int | Id of the matched GitLab user |
|
| `gitlabId` | Int | ID of the matched GitLab user |
|
||||||
|
| `gitlabName` | String | Name of the matched GitLab user |
|
||||||
|
| `gitlabUsername` | String | Username of the matched GitLab user |
|
||||||
| `jiraAccountId` | String! | Account id of the Jira user |
|
| `jiraAccountId` | String! | Account id of the Jira user |
|
||||||
| `jiraDisplayName` | String! | Display name of the Jira user |
|
| `jiraDisplayName` | String! | Display name of the Jira user |
|
||||||
| `jiraEmail` | String | Email of the Jira user, returned only for users with public emails |
|
| `jiraEmail` | String | Email of the Jira user, returned only for users with public emails |
|
||||||
|
|
|
@ -48,7 +48,7 @@ Example response:
|
||||||
"sign_in_text" : null,
|
"sign_in_text" : null,
|
||||||
"container_expiration_policies_enable_historic_entries": true,
|
"container_expiration_policies_enable_historic_entries": true,
|
||||||
"container_registry_token_expire_delay": 5,
|
"container_registry_token_expire_delay": 5,
|
||||||
"repository_storages": ["default"],
|
"repository_storages_weighted": {"default": 100},
|
||||||
"plantuml_enabled": false,
|
"plantuml_enabled": false,
|
||||||
"plantuml_url": null,
|
"plantuml_url": null,
|
||||||
"terminal_max_session_time": 0,
|
"terminal_max_session_time": 0,
|
||||||
|
@ -314,7 +314,8 @@ are listed in the descriptions of the relevant settings.
|
||||||
| `receive_max_input_size` | integer | no | Maximum push size (MB). |
|
| `receive_max_input_size` | integer | no | Maximum push size (MB). |
|
||||||
| `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
|
| `repository_checks_enabled` | boolean | no | GitLab will periodically run `git fsck` in all project and wiki repositories to look for silent disk corruption issues. |
|
||||||
| `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) |
|
| `repository_size_limit` | integer | no | **(PREMIUM)** Size limit per repository (MB) |
|
||||||
| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
|
| `repository_storages` | array of strings | no | (GitLab 13.0 and earlier) List of names of enabled storage paths, taken from `gitlab.yml`. New projects are created in one of these stores, chosen at random. |
|
||||||
|
| `repository_storages_weighted` | hash of strings to integers | no | (GitLab 13.1 and later) Hash of names of taken from `gitlab.yml` to weights. New projects are created in one of these stores, chosen by a weighted random selection. |
|
||||||
| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
|
| `require_two_factor_authentication` | boolean | no | (**If enabled, requires:** `two_factor_grace_period`) Require all users to set up Two-factor authentication. |
|
||||||
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
|
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for groups, projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is `null` which means there is no restriction. |
|
||||||
| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
|
| `rsa_key_restriction` | integer | no | The minimum allowed bit length of an uploaded RSA key. Default is `0` (no restriction). `-1` disables RSA keys. |
|
||||||
|
|
|
@ -111,7 +111,8 @@ Patterns:
|
||||||
- `'"((?:\\"|[^"]|\\")*)"'`: captures terms inside quotes, removing the quotes
|
- `'"((?:\\"|[^"]|\\")*)"'`: captures terms inside quotes, removing the quotes
|
||||||
- `"'((?:\\'|[^']|\\')*)'"`: same as above, for single-quotes
|
- `"'((?:\\'|[^']|\\')*)'"`: same as above, for single-quotes
|
||||||
- `'\.([^.]+)(?=\.|\s|\Z)'`: separate terms with periods in-between
|
- `'\.([^.]+)(?=\.|\s|\Z)'`: separate terms with periods in-between
|
||||||
- `'([\p{L}_.-]+)'` : some common chars in file names to keep the whole filename intact (eg. `my_file-ñame.txt`)
|
- `'([\p{L}_.-]+)'`: some common chars in file names to keep the whole filename intact (eg. `my_file-ñame.txt`)
|
||||||
|
- `'([\p{L}\d_]+)'`: letters, numbers and underscores are the most common tokens in programming. Always capture them greedily regardless of context.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
|
|
|
@ -53,14 +53,14 @@ Be sure to read about [page-specific JavaScript](./performance.md#page-specific-
|
||||||
|
|
||||||
#### Providing data from HAML to JavaScript
|
#### Providing data from HAML to JavaScript
|
||||||
|
|
||||||
While mounting a Vue application may be a need to provide data from Rails to JavaScript.
|
While mounting a Vue application, you might need to provide data from Rails to JavaScript.
|
||||||
To do that, provide the data through `data` attributes in the HTML element and query them while mounting the application.
|
To do that, you can use the `data` attributes in the HTML element and query them while mounting the application.
|
||||||
|
|
||||||
_Note:_ You should only do this while initializing the application, because the mounted element will be replaced with Vue-generated DOM.
|
_Note:_ You should only do this while initializing the application, because the mounted element will be replaced with Vue-generated DOM.
|
||||||
|
|
||||||
The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function
|
The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function
|
||||||
instead of querying the DOM inside the main Vue component is that makes tests easier by avoiding the need to
|
instead of querying the DOM inside the main Vue component is avoiding the need to create a fixture or an HTML element in the unit test,
|
||||||
create a fixture or an HTML element in the unit test. See the following example:
|
which will make the tests easier. See the following example:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// haml
|
// haml
|
||||||
|
@ -134,7 +134,7 @@ This approach has a few benefits:
|
||||||
intermediate components being aware of it (c.f. passing the flag down via
|
intermediate components being aware of it (c.f. passing the flag down via
|
||||||
props).
|
props).
|
||||||
- Good testability, since the flag can be provided to `mount`/`shallowMount`
|
- Good testability, since the flag can be provided to `mount`/`shallowMount`
|
||||||
from `vue-test-utils` as easily as a prop.
|
from `vue-test-utils` simply as a prop.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
@ -155,7 +155,7 @@ This folder holds all components that are specific of this new feature.
|
||||||
If you need to use or create a component that will probably be used somewhere
|
If you need to use or create a component that will probably be used somewhere
|
||||||
else, please refer to `vue_shared/components`.
|
else, please refer to `vue_shared/components`.
|
||||||
|
|
||||||
A good thumb rule to know when you should create a component is to think if
|
A good rule of thumb to know when you should create a component is to think if
|
||||||
it will be reusable elsewhere.
|
it will be reusable elsewhere.
|
||||||
|
|
||||||
For example, tables are used in a quite amount of places across GitLab, a table
|
For example, tables are used in a quite amount of places across GitLab, a table
|
||||||
|
@ -321,7 +321,7 @@ We should verify an event has been fired by asserting against the result of the
|
||||||
|
|
||||||
## Vue.js Expert Role
|
## Vue.js Expert Role
|
||||||
|
|
||||||
One should apply to be a Vue.js expert by opening an MR when the Merge Request's they create and review show:
|
You should only apply to be a Vue.js expert when your own merge requests and your reviews show:
|
||||||
|
|
||||||
- Deep understanding of Vue and Vuex reactivity
|
- Deep understanding of Vue and Vuex reactivity
|
||||||
- Vue and Vuex code are structured according to both official and our guidelines
|
- Vue and Vuex code are structured according to both official and our guidelines
|
||||||
|
|
|
@ -108,6 +108,20 @@ module API
|
||||||
end
|
end
|
||||||
optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner)
|
optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Since we serialize the build output ourselves to ensure Gitaly
|
||||||
|
# gRPC calls succeed, we need a custom Grape format to handle
|
||||||
|
# this:
|
||||||
|
# 1. Grape will ordinarily call `JSON.dump` when Content-Type is set
|
||||||
|
# to application/json. To avoid this, we need to define a custom type in
|
||||||
|
# `content_type` and a custom formatter to go with it.
|
||||||
|
# 2. Grape will parse the request input with the parser defined for
|
||||||
|
# `content_type`. If no such parser exists, it will be treated as text. We
|
||||||
|
# reuse the existing JSON parser to preserve the previous behavior.
|
||||||
|
content_type :build_json, 'application/json'
|
||||||
|
formatter :build_json, ->(object, _) { object }
|
||||||
|
parser :build_json, ::Grape::Parser::Json
|
||||||
|
|
||||||
post '/request' do
|
post '/request' do
|
||||||
authenticate_runner!
|
authenticate_runner!
|
||||||
|
|
||||||
|
@ -128,9 +142,10 @@ module API
|
||||||
result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params)
|
result = ::Ci::RegisterJobService.new(current_runner).execute(runner_params)
|
||||||
|
|
||||||
if result.valid?
|
if result.valid?
|
||||||
if result.build
|
if result.build_json
|
||||||
Gitlab::Metrics.add_event(:build_found)
|
Gitlab::Metrics.add_event(:build_found)
|
||||||
present ::Ci::BuildRunnerPresenter.new(result.build), with: Entities::JobRequest::Response
|
env['api.format'] = :build_json
|
||||||
|
body result.build_json
|
||||||
else
|
else
|
||||||
Gitlab::Metrics.add_event(:build_not_found)
|
Gitlab::Metrics.add_event(:build_not_found)
|
||||||
header 'X-GitLab-Last-Update', new_update
|
header 'X-GitLab-Last-Update', new_update
|
||||||
|
|
|
@ -5054,6 +5054,9 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
|
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Copy API URL"
|
msgid "ClusterIntegration|Copy API URL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import AncestorNotice from '~/clusters_list/components/ancestor_notice.vue';
|
||||||
|
import ClusterStore from '~/clusters_list/store';
|
||||||
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||||
|
|
||||||
|
describe('ClustersAncestorNotice', () => {
|
||||||
|
let store;
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createWrapper = () => {
|
||||||
|
store = ClusterStore({ ancestorHelperPath: '/some/ancestor/path' });
|
||||||
|
wrapper = shallowMount(AncestorNotice, { store, stubs: { GlSprintf } });
|
||||||
|
return wrapper.vm.$nextTick();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
return createWrapper();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when cluster does not have ancestors', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store.state.hasAncestorClusters = false;
|
||||||
|
return wrapper.vm.$nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays no notice', () => {
|
||||||
|
expect(wrapper.isEmpty()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when cluster has ancestors', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store.state.hasAncestorClusters = true;
|
||||||
|
return wrapper.vm.$nextTick();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays notice text', () => {
|
||||||
|
expect(wrapper.text()).toContain(
|
||||||
|
'Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays link', () => {
|
||||||
|
expect(wrapper.contains(GlLink)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
13
spec/graphql/types/jira_user_type_spec.rb
Normal file
13
spec/graphql/types/jira_user_type_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe GitlabSchema.types['JiraUser'] do
|
||||||
|
specify { expect(described_class.graphql_name).to eq('JiraUser') }
|
||||||
|
|
||||||
|
it 'has the expected fields' do
|
||||||
|
expect(described_class).to have_graphql_fields(
|
||||||
|
:jira_account_id, :jira_display_name, :jira_email, :gitlab_id, :gitlab_username, :gitlab_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -60,18 +60,24 @@ RSpec.describe ClustersHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#js_clusters_list_data' do
|
describe '#js_clusters_list_data' do
|
||||||
it 'displays endpoint path and images' do
|
subject { helper.js_clusters_list_data('/path') }
|
||||||
js_data = helper.js_clusters_list_data('/path')
|
|
||||||
|
|
||||||
expect(js_data[:endpoint]).to eq('/path')
|
it 'displays endpoint path' do
|
||||||
|
expect(subject[:endpoint]).to eq('/path')
|
||||||
|
end
|
||||||
|
|
||||||
expect(js_data.dig(:img_tags, :aws, :path)).to match(%r(/illustrations/logos/amazon_eks|svg))
|
it 'generates svg image data', :aggregate_failures do
|
||||||
expect(js_data.dig(:img_tags, :default, :path)).to match(%r(/illustrations/logos/kubernetes|svg))
|
expect(subject.dig(:img_tags, :aws, :path)).to match(%r(/illustrations/logos/amazon_eks|svg))
|
||||||
expect(js_data.dig(:img_tags, :gcp, :path)).to match(%r(/illustrations/logos/google_gke|svg))
|
expect(subject.dig(:img_tags, :default, :path)).to match(%r(/illustrations/logos/kubernetes|svg))
|
||||||
|
expect(subject.dig(:img_tags, :gcp, :path)).to match(%r(/illustrations/logos/google_gke|svg))
|
||||||
|
|
||||||
expect(js_data.dig(:img_tags, :aws, :text)).to eq('Amazon EKS')
|
expect(subject.dig(:img_tags, :aws, :text)).to eq('Amazon EKS')
|
||||||
expect(js_data.dig(:img_tags, :default, :text)).to eq('Kubernetes Cluster')
|
expect(subject.dig(:img_tags, :default, :text)).to eq('Kubernetes Cluster')
|
||||||
expect(js_data.dig(:img_tags, :gcp, :text)).to eq('Google GKE')
|
expect(subject.dig(:img_tags, :gcp, :text)).to eq('Google GKE')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays and ancestor_help_path' do
|
||||||
|
expect(subject[:ancestor_help_path]).to eq('/help/user/group/clusters/index#cluster-precedence')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -518,6 +518,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
request_job info: { platform: :darwin }
|
request_job info: { platform: :darwin }
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:created)
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
expect(response.headers['Content-Type']).to eq('application/json')
|
||||||
expect(response.headers).not_to have_key('X-GitLab-Last-Update')
|
expect(response.headers).not_to have_key('X-GitLab-Last-Update')
|
||||||
expect(runner.reload.platform).to eq('darwin')
|
expect(runner.reload.platform).to eq('darwin')
|
||||||
expect(json_response['id']).to eq(job.id)
|
expect(json_response['id']).to eq(job.id)
|
||||||
|
@ -569,6 +570,24 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when a Gitaly exception is thrown during response' do
|
||||||
|
before do
|
||||||
|
allow_next_instance_of(Ci::BuildRunnerPresenter) do |instance|
|
||||||
|
allow(instance).to receive(:artifacts).and_raise(GRPC::DeadlineExceeded)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails the job as a scheduler failure' do
|
||||||
|
request_job
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
expect(job.reload.failed?).to be_truthy
|
||||||
|
expect(job.failure_reason).to eq('scheduler_failure')
|
||||||
|
expect(job.runner_id).to eq(runner.id)
|
||||||
|
expect(job.runner_session).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
|
context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
|
||||||
before do
|
before do
|
||||||
project.update!(ci_default_git_depth: nil)
|
project.update!(ci_default_git_depth: nil)
|
||||||
|
@ -1090,7 +1109,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
||||||
|
|
||||||
def request_job(token = runner.token, **params)
|
def request_job(token = runner.token, **params)
|
||||||
new_params = params.merge(token: token, last_update: last_update)
|
new_params = params.merge(token: token, last_update: last_update)
|
||||||
post api('/jobs/request'), params: new_params, headers: { 'User-Agent' => user_agent }
|
post api('/jobs/request'), params: new_params.to_json, headers: { 'User-Agent' => user_agent, 'Content-Type': 'application/json' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -109,12 +109,14 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'shared runner' do
|
context 'shared runner' do
|
||||||
let(:build) { execute(shared_runner) }
|
let(:response) { described_class.new(shared_runner).execute }
|
||||||
|
let(:build) { response.build }
|
||||||
|
|
||||||
it { expect(build).to be_kind_of(Build) }
|
it { expect(build).to be_kind_of(Build) }
|
||||||
it { expect(build).to be_valid }
|
it { expect(build).to be_valid }
|
||||||
it { expect(build).to be_running }
|
it { expect(build).to be_running }
|
||||||
it { expect(build.runner).to eq(shared_runner) }
|
it { expect(build.runner).to eq(shared_runner) }
|
||||||
|
it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'specific runner' do
|
context 'specific runner' do
|
||||||
|
|
|
@ -29,9 +29,9 @@ RSpec.describe JiraImport::UsersMapper do
|
||||||
# mapping is tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/219023
|
# mapping is tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/219023
|
||||||
let(:mapped_users) do
|
let(:mapped_users) do
|
||||||
[
|
[
|
||||||
{ jira_account_id: 'abcd', jira_display_name: 'user1', jira_email: nil, gitlab_id: nil },
|
{ jira_account_id: 'abcd', jira_display_name: 'user1', jira_email: nil, gitlab_id: nil, gitlab_username: nil, gitlab_name: nil },
|
||||||
{ jira_account_id: 'efg', jira_display_name: nil, jira_email: nil, gitlab_id: nil },
|
{ jira_account_id: 'efg', jira_display_name: nil, jira_email: nil, gitlab_id: nil, gitlab_username: nil, gitlab_name: nil },
|
||||||
{ jira_account_id: 'hij', jira_display_name: 'user3', jira_email: 'user3@example.com', gitlab_id: nil }
|
{ jira_account_id: 'hij', jira_display_name: 'user3', jira_email: 'user3@example.com', gitlab_id: nil, gitlab_username: nil, gitlab_name: nil }
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue