Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
73391dcc36
commit
e0b84f4ba4
|
@ -86,4 +86,4 @@ jsdoc/
|
||||||
.projections.json
|
.projections.json
|
||||||
/qa/.rakeTasks
|
/qa/.rakeTasks
|
||||||
webpack-dev-server.json
|
webpack-dev-server.json
|
||||||
.nvimrc
|
/.nvimrc
|
||||||
|
|
|
@ -3,11 +3,13 @@ import { mapGetters } from 'vuex';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import store from '~/pipelines/stores/test_reports';
|
import store from '~/pipelines/stores/test_reports';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TestsSuiteTable',
|
name: 'TestsSuiteTable',
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
|
SmartVirtualList,
|
||||||
},
|
},
|
||||||
store,
|
store,
|
||||||
props: {
|
props: {
|
||||||
|
@ -23,6 +25,8 @@ export default {
|
||||||
return this.getSuiteTests.length > 0;
|
return this.getSuiteTests.length > 0;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
maxShownRows: 30,
|
||||||
|
typicalRowHeight: 75,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -34,7 +38,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="hasSuites" class="test-reports-table js-test-cases-table">
|
<div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-cases-table">
|
||||||
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray">
|
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray">
|
||||||
<div role="rowheader" class="table-section section-20">
|
<div role="rowheader" class="table-section section-20">
|
||||||
{{ __('Class') }}
|
{{ __('Class') }}
|
||||||
|
@ -53,52 +57,58 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<smart-virtual-list
|
||||||
v-for="(testCase, index) in getSuiteTests"
|
:length="getSuiteTests.length"
|
||||||
:key="index"
|
:remain="$options.maxShownRows"
|
||||||
class="gl-responsive-table-row rounded align-items-md-start mt-sm-3 js-case-row"
|
:size="$options.typicalRowHeight"
|
||||||
>
|
>
|
||||||
<div class="table-section section-20 section-wrap">
|
<div
|
||||||
<div role="rowheader" class="table-mobile-header">{{ __('Class') }}</div>
|
v-for="(testCase, index) in getSuiteTests"
|
||||||
<div class="table-mobile-content pr-md-1">{{ testCase.classname }}</div>
|
:key="index"
|
||||||
</div>
|
class="gl-responsive-table-row rounded align-items-md-start mt-xs-3 js-case-row"
|
||||||
|
>
|
||||||
|
<div class="table-section section-20 section-wrap">
|
||||||
|
<div role="rowheader" class="table-mobile-header">{{ __('Class') }}</div>
|
||||||
|
<div class="table-mobile-content pr-md-1 text-truncate">{{ testCase.classname }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-section section-20 section-wrap">
|
<div class="table-section section-20 section-wrap">
|
||||||
<div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
<div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
||||||
<div class="table-mobile-content">{{ testCase.name }}</div>
|
<div class="table-mobile-content">{{ testCase.name }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-section section-10 section-wrap">
|
<div class="table-section section-10 section-wrap">
|
||||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||||
<div class="table-mobile-content text-center">
|
<div class="table-mobile-content text-center">
|
||||||
<div
|
<div
|
||||||
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||||
:class="`ci-status-icon-${testCase.status}`"
|
:class="`ci-status-icon-${testCase.status}`"
|
||||||
>
|
>
|
||||||
<icon :size="24" :name="testCase.icon" />
|
<icon :size="24" :name="testCase.icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-section flex-grow-1">
|
||||||
|
<div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
|
||||||
|
<div class="table-mobile-content">
|
||||||
|
<pre
|
||||||
|
v-if="testCase.system_output"
|
||||||
|
class="build-trace build-trace-rounded text-left"
|
||||||
|
><code class="bash p-0">{{testCase.system_output}}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-section section-10 section-wrap">
|
||||||
|
<div role="rowheader" class="table-mobile-header">
|
||||||
|
{{ __('Duration') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content text-right pr-sm-1">
|
||||||
|
{{ testCase.formattedTime }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</smart-virtual-list>
|
||||||
<div class="table-section flex-grow-1">
|
|
||||||
<div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
|
|
||||||
<div class="table-mobile-content">
|
|
||||||
<pre
|
|
||||||
v-if="testCase.system_output"
|
|
||||||
class="build-trace build-trace-rounded text-left"
|
|
||||||
><code class="bash p-0">{{testCase.system_output}}</code></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 section-wrap">
|
|
||||||
<div role="rowheader" class="table-mobile-header">
|
|
||||||
{{ __('Duration') }}
|
|
||||||
</div>
|
|
||||||
<div class="table-mobile-content text-right">
|
|
||||||
{{ testCase.formattedTime }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import store from '~/pipelines/stores/test_reports';
|
import store from '~/pipelines/stores/test_reports';
|
||||||
|
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TestsSummaryTable',
|
name: 'TestsSummaryTable',
|
||||||
|
components: {
|
||||||
|
SmartVirtualList,
|
||||||
|
},
|
||||||
store,
|
store,
|
||||||
props: {
|
props: {
|
||||||
heading: {
|
heading: {
|
||||||
|
@ -24,6 +28,8 @@ export default {
|
||||||
this.$emit('row-click', suite);
|
this.$emit('row-click', suite);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
maxShownRows: 20,
|
||||||
|
typicalRowHeight: 55,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -35,7 +41,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="hasSuites" class="test-reports-table js-test-suites-table">
|
<div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-suites-table">
|
||||||
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold">
|
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold">
|
||||||
<div role="rowheader" class="table-section section-25 pl-3">
|
<div role="rowheader" class="table-section section-25 pl-3">
|
||||||
{{ __('Suite') }}
|
{{ __('Suite') }}
|
||||||
|
@ -60,66 +66,72 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<smart-virtual-list
|
||||||
v-for="(testSuite, index) in getTestSuites"
|
:length="getTestSuites.length"
|
||||||
:key="index"
|
:remain="$options.maxShownRows"
|
||||||
role="row"
|
:size="$options.typicalRowHeight"
|
||||||
class="gl-responsive-table-row gl-responsive-table-row-clickable test-reports-summary-row rounded cursor-pointer js-suite-row"
|
|
||||||
@click="tableRowClick(testSuite)"
|
|
||||||
>
|
>
|
||||||
<div class="table-section section-25">
|
<div
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
v-for="(testSuite, index) in getTestSuites"
|
||||||
{{ __('Suite') }}
|
:key="index"
|
||||||
|
role="row"
|
||||||
|
class="gl-responsive-table-row gl-responsive-table-row-clickable test-reports-summary-row rounded cursor-pointer js-suite-row"
|
||||||
|
@click="tableRowClick(testSuite)"
|
||||||
|
>
|
||||||
|
<div class="table-section section-25">
|
||||||
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
|
{{ __('Suite') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content underline cgray pl-3">
|
||||||
|
{{ testSuite.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content underline cgray pl-3">
|
|
||||||
{{ testSuite.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-25">
|
<div class="table-section section-25">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Duration') }}
|
{{ __('Duration') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content text-md-left">
|
||||||
|
{{ testSuite.formattedTime }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content text-md-left">
|
|
||||||
{{ testSuite.formattedTime }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 text-center">
|
<div class="table-section section-10 text-center">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Failed') }}
|
{{ __('Failed') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 text-center">
|
<div class="table-section section-10 text-center">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Errors') }}
|
{{ __('Errors') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 text-center">
|
<div class="table-section section-10 text-center">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Skipped') }}
|
{{ __('Skipped') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 text-center">
|
<div class="table-section section-10 text-center">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Passed') }}
|
{{ __('Passed') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-section section-10 text-right pr-md-3">
|
<div class="table-section section-10 text-right pr-md-3">
|
||||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||||
{{ __('Total') }}
|
{{ __('Total') }}
|
||||||
|
</div>
|
||||||
|
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</smart-virtual-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
|
@ -57,20 +57,24 @@ class GitlabSchema < GraphQL::Schema
|
||||||
object.to_global_id
|
object.to_global_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Find an object by looking it up from its global ID, passed as a string.
|
||||||
|
#
|
||||||
|
# This is the composition of 'parse_gid' and 'find_by_gid', see these
|
||||||
|
# methods for further documentation.
|
||||||
def object_from_id(global_id, ctx = {})
|
def object_from_id(global_id, ctx = {})
|
||||||
expected_type = ctx[:expected_type]
|
gid = parse_gid(global_id, ctx)
|
||||||
gid = GlobalID.parse(global_id)
|
|
||||||
|
|
||||||
unless gid
|
find_by_gid(gid)
|
||||||
raise Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab id."
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if expected_type && !gid.model_class.ancestors.include?(expected_type)
|
|
||||||
vars = { global_id: global_id, expected_type: expected_type }
|
|
||||||
msg = _('%{global_id} is not a valid id for %{expected_type}.') % vars
|
|
||||||
raise Gitlab::Graphql::Errors::ArgumentError, msg
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Find an object by looking it up from its 'GlobalID'.
|
||||||
|
#
|
||||||
|
# * For `ApplicationRecord`s, this is equivalent to
|
||||||
|
# `global_id.model_class.find(gid.model_id)`, but more efficient.
|
||||||
|
# * For classes that implement `.lazy_find(global_id)`, this class method
|
||||||
|
# will be called.
|
||||||
|
# * All other classes will use `GlobalID#find`
|
||||||
|
def find_by_gid(gid)
|
||||||
if gid.model_class < ApplicationRecord
|
if gid.model_class < ApplicationRecord
|
||||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
|
Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
|
||||||
elsif gid.model_class.respond_to?(:lazy_find)
|
elsif gid.model_class.respond_to?(:lazy_find)
|
||||||
|
@ -80,6 +84,38 @@ class GitlabSchema < GraphQL::Schema
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Parse a string to a GlobalID, raising ArgumentError if there are problems
|
||||||
|
# with it.
|
||||||
|
#
|
||||||
|
# Problems that may occur:
|
||||||
|
# * it may not be syntactically valid
|
||||||
|
# * it may not match the expected type (see below)
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# * :expected_type [Class] - the type of object this GlobalID should refer to.
|
||||||
|
#
|
||||||
|
# e.g.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# gid = GitlabSchema.parse_gid(my_string, expected_type: ::Project)
|
||||||
|
# project_id = gid.model_id
|
||||||
|
# gid.model_class == ::Project
|
||||||
|
# ```
|
||||||
|
def parse_gid(global_id, ctx = {})
|
||||||
|
expected_type = ctx[:expected_type]
|
||||||
|
gid = GlobalID.parse(global_id)
|
||||||
|
|
||||||
|
raise Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab id." unless gid
|
||||||
|
|
||||||
|
if expected_type && !gid.model_class.ancestors.include?(expected_type)
|
||||||
|
vars = { global_id: global_id, expected_type: expected_type }
|
||||||
|
msg = _('%{global_id} is not a valid id for %{expected_type}.') % vars
|
||||||
|
raise Gitlab::Graphql::Errors::ArgumentError, msg
|
||||||
|
end
|
||||||
|
|
||||||
|
gid
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def max_query_complexity(ctx)
|
def max_query_complexity(ctx)
|
||||||
|
|
|
@ -5,7 +5,7 @@ require 'securerandom'
|
||||||
module Clusters
|
module Clusters
|
||||||
module Applications
|
module Applications
|
||||||
class Jupyter < ApplicationRecord
|
class Jupyter < ApplicationRecord
|
||||||
VERSION = '0.9-174bbd5'
|
VERSION = '0.9.0-beta.2'
|
||||||
|
|
||||||
self.table_name = 'clusters_applications_jupyter'
|
self.table_name = 'clusters_applications_jupyter'
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,20 @@ class ExternalWikiService < Service
|
||||||
|
|
||||||
def fields
|
def fields
|
||||||
[
|
[
|
||||||
{ type: 'text', name: 'external_wiki_url', placeholder: s_('ExternalWikiService|The URL of the external Wiki'), required: true }
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'external_wiki_url',
|
||||||
|
placeholder: s_('ExternalWikiService|The URL of the external Wiki'),
|
||||||
|
required: true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(_data)
|
def execute(_data)
|
||||||
@response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true) rescue nil
|
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
|
||||||
if @response != 200
|
response.body if response.code == 200
|
||||||
nil
|
rescue
|
||||||
end
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.supported_events
|
def self.supported_events
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Container expiration policies can be updated with the project api
|
||||||
|
merge_request: 22180
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Check both DEPENDENCY_SCANNING_DISABLED and DS_DISABLE_DIND when executing Dependency Scanning job template
|
||||||
|
merge_request: 22172
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Added smart virtual list component to test reports to enhance rendering performance
|
||||||
|
merge_request: 22381
|
||||||
|
author:
|
||||||
|
type: performance
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Document MAVEN_CLI_OPTS defaults for maven project dependency scanning and update when the variable is used
|
||||||
|
merge_request: 22126
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Update jupyterhub chart
|
||||||
|
merge_request: 22127
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -25,15 +25,22 @@ The most common architecture for Praefect is simplified in the diagram below:
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
GitLab --> Praefect;
|
GitLab --> Praefect;
|
||||||
Praefect --> Gitaly-1;
|
Praefect --- PostgreSQL;
|
||||||
Praefect --> Gitaly-2;
|
Praefect --> Gitaly1;
|
||||||
Praefect --> Gitaly-3;
|
Praefect --> Gitaly2;
|
||||||
|
Praefect --> Gitaly3;
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `GitLab` is the collection of clients that can request Git operations.
|
Where `GitLab` is the collection of clients that can request Git operations.
|
||||||
The Praefect node has three storage nodes attached. Praefect itself doesn't
|
The Praefect node has three storage nodes attached. Praefect itself doesn't
|
||||||
store data, but connects to three Gitaly nodes, `Gitaly-1`, `Gitaly-2`, and `Gitaly-3`.
|
store data, but connects to three Gitaly nodes, `Gitaly-1`, `Gitaly-2`, and `Gitaly-3`.
|
||||||
|
|
||||||
|
In order to keep track of replication state, Praefect relies on a
|
||||||
|
PostgreSQL database. This database is a single point of failure so you
|
||||||
|
should use a highly available PostgreSQL server for this. GitLab
|
||||||
|
itself needs a HA PostgreSQL server too, so you could optionally co-locate the Praefect
|
||||||
|
SQL database on the PostgreSQL server you use for the rest of GitLab.
|
||||||
|
|
||||||
Praefect may be enabled on its own node or can be run on the GitLab server.
|
Praefect may be enabled on its own node or can be run on the GitLab server.
|
||||||
In the example below we will use a separate server, but the optimal configuration
|
In the example below we will use a separate server, but the optimal configuration
|
||||||
for Praefect is still being determined.
|
for Praefect is still being determined.
|
||||||
|
@ -62,6 +69,53 @@ We need to manage the following secrets and make them match across hosts:
|
||||||
`PRAEFECT_EXTERNAL_TOKEN` because Gitaly clients must not be able to
|
`PRAEFECT_EXTERNAL_TOKEN` because Gitaly clients must not be able to
|
||||||
access internal nodes of the Praefect cluster directly; that could
|
access internal nodes of the Praefect cluster directly; that could
|
||||||
lead to data loss.
|
lead to data loss.
|
||||||
|
1. `PRAEFECT_SQL_PASSWORD`: this password is used by Praefect to connect to
|
||||||
|
PostgreSQL.
|
||||||
|
|
||||||
|
#### Network addresses
|
||||||
|
|
||||||
|
1. `POSTGRESQL_SERVER`: the host name or IP address of your PostgreSQL server
|
||||||
|
|
||||||
|
#### PostgreSQL
|
||||||
|
|
||||||
|
To set up a Praefect cluster you need a highly available PostgreSQL
|
||||||
|
server. You need PostgreSQL 9.6 or newer. Praefect needs to have a SQL
|
||||||
|
user with the right to create databases.
|
||||||
|
|
||||||
|
In the instructions below we assume you have administrative access to
|
||||||
|
your PostgreSQL server via `psql`. Depending on your environment, you
|
||||||
|
may also be able to do this via the web interface of your cloud
|
||||||
|
platform, or via your configuration management system, etc.
|
||||||
|
|
||||||
|
Below we assume that you have administrative access as the `postgres`
|
||||||
|
user. First open a `psql` session as the `postgres` user:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
psql -h POSTGRESQL_SERVER -U postgres -d template1
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you are connected, run the following command. Replace
|
||||||
|
`PRAEFECT_SQL_PASSWORD` with the actual (random) password you
|
||||||
|
generated for the `praefect` SQL user:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE ROLE praefect WITH LOGIN CREATEDB PASSWORD 'PRAEFECT_SQL_PASSWORD';
|
||||||
|
\q # exit psql
|
||||||
|
```
|
||||||
|
|
||||||
|
Now connect as the `praefect` user to create the database. This has
|
||||||
|
the side effect of verifying that you have access:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
psql -h POSTGRESQL_SERVER -U praefect -d template1
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have connected as the `praefect` user, run:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE praefect_production WITH ENCODING=UTF8;
|
||||||
|
\q # quit psql
|
||||||
|
```
|
||||||
|
|
||||||
#### Praefect
|
#### Praefect
|
||||||
|
|
||||||
|
@ -118,10 +172,39 @@ praefect['virtual_storages'] = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
praefect['database_host'] = 'POSTGRESQL_SERVER'
|
||||||
|
praefect['database_port'] = 5432
|
||||||
|
praefect['database_user'] = 'praefect'
|
||||||
|
praefect['database_password'] = 'PRAEFECT_SQL_PASSWORD'
|
||||||
|
praefect['database_dbname'] = 'praefect_production'
|
||||||
|
|
||||||
|
# Uncomment the line below if you do not want to use an encrypted
|
||||||
|
# connection to PostgreSQL
|
||||||
|
# praefect['database_sslmode'] = 'disable'
|
||||||
|
|
||||||
|
# Uncomment and modify these lines if you are using a TLS client
|
||||||
|
# certificate to connect to PostgreSQL
|
||||||
|
# praefect['database_sslcert'] = '/path/to/client-cert'
|
||||||
|
# praefect['database_sslkey'] = '/path/to/client-key'
|
||||||
|
|
||||||
|
# Uncomment and modify this line if your PostgreSQL server uses a custom
|
||||||
|
# CA
|
||||||
|
# praefect['database_sslrootcert'] = '/path/to/rootcert'
|
||||||
```
|
```
|
||||||
|
|
||||||
Save the file and [reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
Save the file and [reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||||
|
|
||||||
|
After you reconfigure, verify that Praefect can reach PostgreSQL:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -u git /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-ping
|
||||||
|
```
|
||||||
|
|
||||||
|
If the check fails, make sure you have followed the steps correctly. If you edit `/etc/gitlab/gitlab.rb`,
|
||||||
|
remember to run `sudo gitlab-ctl reconfigure` again before trying the
|
||||||
|
`sql-ping` command.
|
||||||
|
|
||||||
#### Gitaly
|
#### Gitaly
|
||||||
|
|
||||||
Next we will configure each Gitaly server assigned to Praefect. Configuration for these
|
Next we will configure each Gitaly server assigned to Praefect. Configuration for these
|
||||||
|
|
|
@ -631,7 +631,7 @@ mounting the docker-daemon and setting `privileged = false` in the Runner's
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[runners.docker]
|
[runners.docker]
|
||||||
image = "ruby:2.1"
|
image = "ruby:2.6"
|
||||||
privileged = false
|
privileged = false
|
||||||
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
|
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
|
||||||
```
|
```
|
||||||
|
|
|
@ -529,9 +529,9 @@ type CreateSnippetPayload {
|
||||||
snippet: Snippet
|
snippet: Snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
type Design implements Noteable {
|
type Design implements DesignFields & Noteable {
|
||||||
"""
|
"""
|
||||||
Diff refs of the design
|
The diff refs for this design
|
||||||
"""
|
"""
|
||||||
diffRefs: DiffRefs!
|
diffRefs: DiffRefs!
|
||||||
|
|
||||||
|
@ -561,33 +561,32 @@ type Design implements Noteable {
|
||||||
): DiscussionConnection!
|
): DiscussionConnection!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Type of change made to the design at the version specified by the `atVersion`
|
How this design was changed in the current version
|
||||||
argument if supplied. Defaults to the latest version
|
|
||||||
"""
|
"""
|
||||||
event: DesignVersionEvent!
|
event: DesignVersionEvent!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Filename of the design file
|
The filename of the design
|
||||||
"""
|
"""
|
||||||
filename: String!
|
filename: String!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Full path of the design file
|
The full path to the design file
|
||||||
"""
|
"""
|
||||||
fullPath: String!
|
fullPath: String!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ID of the design
|
The ID of this design
|
||||||
"""
|
"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Image of the design
|
The URL of the image
|
||||||
"""
|
"""
|
||||||
image: String!
|
image: String!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Issue associated with the design
|
The issue the design belongs to
|
||||||
"""
|
"""
|
||||||
issue: Issue!
|
issue: Issue!
|
||||||
|
|
||||||
|
@ -617,17 +616,17 @@ type Design implements Noteable {
|
||||||
): NoteConnection!
|
): NoteConnection!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Total count of user-created notes for the design
|
The total count of user-created notes for this design
|
||||||
"""
|
"""
|
||||||
notesCount: Int!
|
notesCount: Int!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Project associated with the design
|
The project the design belongs to
|
||||||
"""
|
"""
|
||||||
project: Project!
|
project: Project!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
All versions related to the design, ordered newest first
|
All versions related to this design ordered newest first
|
||||||
"""
|
"""
|
||||||
versions(
|
versions(
|
||||||
"""
|
"""
|
||||||
|
@ -765,6 +764,53 @@ type DesignEdge {
|
||||||
node: Design
|
node: Design
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DesignFields {
|
||||||
|
"""
|
||||||
|
The diff refs for this design
|
||||||
|
"""
|
||||||
|
diffRefs: DiffRefs!
|
||||||
|
|
||||||
|
"""
|
||||||
|
How this design was changed in the current version
|
||||||
|
"""
|
||||||
|
event: DesignVersionEvent!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The filename of the design
|
||||||
|
"""
|
||||||
|
filename: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The full path to the design file
|
||||||
|
"""
|
||||||
|
fullPath: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The ID of this design
|
||||||
|
"""
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The URL of the image
|
||||||
|
"""
|
||||||
|
image: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The issue the design belongs to
|
||||||
|
"""
|
||||||
|
issue: Issue!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The total count of user-created notes for this design
|
||||||
|
"""
|
||||||
|
notesCount: Int!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The project the design belongs to
|
||||||
|
"""
|
||||||
|
project: Project!
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Autogenerated input type of DesignManagementDelete
|
Autogenerated input type of DesignManagementDelete
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -10350,7 +10350,7 @@
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"name": "diffRefs",
|
"name": "diffRefs",
|
||||||
"description": "Diff refs of the design",
|
"description": "The diff refs for this design",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10425,7 +10425,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "event",
|
"name": "event",
|
||||||
"description": "Type of change made to the design at the version specified by the `atVersion` argument if supplied. Defaults to the latest version",
|
"description": "How this design was changed in the current version",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10443,7 +10443,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "filename",
|
"name": "filename",
|
||||||
"description": "Filename of the design file",
|
"description": "The filename of the design",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10461,7 +10461,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fullPath",
|
"name": "fullPath",
|
||||||
"description": "Full path of the design file",
|
"description": "The full path to the design file",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10479,7 +10479,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"description": "ID of the design",
|
"description": "The ID of this design",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10497,7 +10497,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "image",
|
"name": "image",
|
||||||
"description": "Image of the design",
|
"description": "The URL of the image",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10515,7 +10515,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "issue",
|
"name": "issue",
|
||||||
"description": "Issue associated with the design",
|
"description": "The issue the design belongs to",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10590,7 +10590,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "notesCount",
|
"name": "notesCount",
|
||||||
"description": "Total count of user-created notes for the design",
|
"description": "The total count of user-created notes for this design",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10608,7 +10608,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "project",
|
"name": "project",
|
||||||
"description": "Project associated with the design",
|
"description": "The project the design belongs to",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
@ -10626,7 +10626,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "versions",
|
"name": "versions",
|
||||||
"description": "All versions related to the design, ordered newest first",
|
"description": "All versions related to this design ordered newest first",
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "after",
|
"name": "after",
|
||||||
|
@ -10688,11 +10688,195 @@
|
||||||
"kind": "INTERFACE",
|
"kind": "INTERFACE",
|
||||||
"name": "Noteable",
|
"name": "Noteable",
|
||||||
"ofType": null
|
"ofType": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "INTERFACE",
|
||||||
|
"name": "DesignFields",
|
||||||
|
"ofType": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "INTERFACE",
|
||||||
|
"name": "DesignFields",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "diffRefs",
|
||||||
|
"description": "The diff refs for this design",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "DiffRefs",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "event",
|
||||||
|
"description": "How this design was changed in the current version",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "DesignVersionEvent",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "filename",
|
||||||
|
"description": "The filename of the design",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fullPath",
|
||||||
|
"description": "The full path to the design file",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"description": "The ID of this design",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"description": "The URL of the image",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "issue",
|
||||||
|
"description": "The issue the design belongs to",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Issue",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "notesCount",
|
||||||
|
"description": "The total count of user-created notes for this design",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Int",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "project",
|
||||||
|
"description": "The project the design belongs to",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Project",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": [
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Design",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "DesignVersionEvent",
|
"name": "DesignVersionEvent",
|
||||||
|
|
|
@ -104,15 +104,15 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| --- | ---- | ---------- |
|
| --- | ---- | ---------- |
|
||||||
| `id` | ID! | ID of the design |
|
| `id` | ID! | The ID of this design |
|
||||||
| `project` | Project! | Project associated with the design |
|
| `project` | Project! | The project the design belongs to |
|
||||||
| `issue` | Issue! | Issue associated with the design |
|
| `issue` | Issue! | The issue the design belongs to |
|
||||||
| `notesCount` | Int! | Total count of user-created notes for the design |
|
| `filename` | String! | The filename of the design |
|
||||||
| `filename` | String! | Filename of the design file |
|
| `fullPath` | String! | The full path to the design file |
|
||||||
| `fullPath` | String! | Full path of the design file |
|
| `image` | String! | The URL of the image |
|
||||||
| `event` | DesignVersionEvent! | Type of change made to the design at the version specified by the `atVersion` argument if supplied. Defaults to the latest version |
|
| `diffRefs` | DiffRefs! | The diff refs for this design |
|
||||||
| `image` | String! | Image of the design |
|
| `event` | DesignVersionEvent! | How this design was changed in the current version |
|
||||||
| `diffRefs` | DiffRefs! | Diff refs of the design |
|
| `notesCount` | Int! | The total count of user-created notes for this design |
|
||||||
|
|
||||||
### DesignCollection
|
### DesignCollection
|
||||||
|
|
||||||
|
|
|
@ -761,6 +761,14 @@ GET /projects/:id
|
||||||
"snippets_enabled": false,
|
"snippets_enabled": false,
|
||||||
"resolve_outdated_diff_discussions": false,
|
"resolve_outdated_diff_discussions": false,
|
||||||
"container_registry_enabled": false,
|
"container_registry_enabled": false,
|
||||||
|
"container_expiration_policy": {
|
||||||
|
"cadence": "7d",
|
||||||
|
"enabled": false,
|
||||||
|
"keep_n": null,
|
||||||
|
"older_than": null,
|
||||||
|
"name_regex": null,
|
||||||
|
"next_run_at": "2020-01-07T21:42:58.658Z"
|
||||||
|
},
|
||||||
"created_at": "2013-09-30T13:46:02Z",
|
"created_at": "2013-09-30T13:46:02Z",
|
||||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||||
"creator_id": 3,
|
"creator_id": 3,
|
||||||
|
@ -986,6 +994,7 @@ POST /projects
|
||||||
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||||
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
||||||
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
|
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
|
||||||
|
| `container_expiration_policy_attributes` | hash | no | Update the container expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `enabled` (boolean) |
|
||||||
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
|
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
|
||||||
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
||||||
| `import_url` | string | no | URL to import repository from |
|
| `import_url` | string | no | URL to import repository from |
|
||||||
|
@ -1115,6 +1124,7 @@ PUT /projects/:id
|
||||||
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||||
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
||||||
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
|
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
|
||||||
|
| `container_expiration_policy_attributes` | hash | no | Update the container expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `enabled` (boolean) |
|
||||||
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
|
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
|
||||||
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
||||||
| `import_url` | string | no | URL to import repository from |
|
| `import_url` | string | no | URL to import repository from |
|
||||||
|
|
|
@ -32,14 +32,14 @@ A one-line example can be seen below:
|
||||||
sudo gitlab-runner register \
|
sudo gitlab-runner register \
|
||||||
--url "https://gitlab.example.com/" \
|
--url "https://gitlab.example.com/" \
|
||||||
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
||||||
--description "docker-ruby-2.1" \
|
--description "docker-ruby:2.6" \
|
||||||
--executor "docker" \
|
--executor "docker" \
|
||||||
--docker-image ruby:2.1 \
|
--docker-image ruby:2.6 \
|
||||||
--docker-services postgres:latest \
|
--docker-services postgres:latest \
|
||||||
--docker-services mysql:latest
|
--docker-services mysql:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
The registered runner will use the `ruby:2.1` Docker image and will run two
|
The registered runner will use the `ruby:2.6` Docker image and will run two
|
||||||
services, `postgres:latest` and `mysql:latest`, both of which will be
|
services, `postgres:latest` and `mysql:latest`, both of which will be
|
||||||
accessible during the build process.
|
accessible during the build process.
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ services that you want to use during build time:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
default:
|
default:
|
||||||
image: ruby:2.2
|
image: ruby:2.6
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- postgres:9.3
|
- postgres:9.3
|
||||||
|
@ -214,15 +214,15 @@ default:
|
||||||
before_script:
|
before_script:
|
||||||
- bundle install
|
- bundle install
|
||||||
|
|
||||||
test:2.1:
|
test:2.6:
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
services:
|
services:
|
||||||
- postgres:9.3
|
- postgres:9.3
|
||||||
script:
|
script:
|
||||||
- bundle exec rake spec
|
- bundle exec rake spec
|
||||||
|
|
||||||
test:2.2:
|
test:2.7:
|
||||||
image: ruby:2.2
|
image: ruby:2.7
|
||||||
services:
|
services:
|
||||||
- postgres:9.4
|
- postgres:9.4
|
||||||
script:
|
script:
|
||||||
|
@ -235,7 +235,7 @@ for `image` and `services`:
|
||||||
```yaml
|
```yaml
|
||||||
default:
|
default:
|
||||||
image:
|
image:
|
||||||
name: ruby:2.2
|
name: ruby:2.6
|
||||||
entrypoint: ["/bin/bash"]
|
entrypoint: ["/bin/bash"]
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
@ -277,7 +277,7 @@ services:
|
||||||
command: ["postgres"]
|
command: ["postgres"]
|
||||||
|
|
||||||
image:
|
image:
|
||||||
name: ruby:2.2
|
name: ruby:2.6
|
||||||
entrypoint: ["/bin/bash"]
|
entrypoint: ["/bin/bash"]
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
@ -773,7 +773,7 @@ time.
|
||||||
|
|
||||||
1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`.
|
1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`.
|
||||||
1. Create cache container to store all volumes as defined in `config.toml` and
|
1. Create cache container to store all volumes as defined in `config.toml` and
|
||||||
`Dockerfile` of build image (`ruby:2.1` as in above example).
|
`Dockerfile` of build image (`ruby:2.6` as in above example).
|
||||||
1. Create build container and link any service container to build container.
|
1. Create build container and link any service container to build container.
|
||||||
1. Start build container and send job script to the container.
|
1. Start build container and send job script to the container.
|
||||||
1. Run job script.
|
1. Run job script.
|
||||||
|
@ -818,11 +818,11 @@ Finally, create a build container by executing the `build_script` file we
|
||||||
created earlier:
|
created earlier:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
|
docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.6 /bin/bash < build_script
|
||||||
```
|
```
|
||||||
|
|
||||||
The above command will create a container named `build` that is spawned from
|
The above command will create a container named `build` that is spawned from
|
||||||
the `ruby:2.1` image and has two services linked to it. The `build_script` is
|
the `ruby:2.6` image and has two services linked to it. The `build_script` is
|
||||||
piped using STDIN to the bash interpreter which in turn executes the
|
piped using STDIN to the bash interpreter which in turn executes the
|
||||||
`build_script` in the `build` container.
|
`build_script` in the `build` container.
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,12 @@ gitlab-runner register \
|
||||||
--non-interactive \
|
--non-interactive \
|
||||||
--url "https://gitlab.com/" \
|
--url "https://gitlab.com/" \
|
||||||
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
||||||
--description "ruby-2.2" \
|
--description "ruby:2.6" \
|
||||||
--executor "docker" \
|
--executor "docker" \
|
||||||
--docker-image ruby:2.2 \
|
--docker-image ruby:2.6 \
|
||||||
--docker-postgres latest
|
--docker-postgres latest
|
||||||
```
|
```
|
||||||
|
|
||||||
With the command above, you create a Runner that uses the [ruby:2.2](https://hub.docker.com/_/ruby) image and uses a [postgres](https://hub.docker.com/_/postgres) database.
|
With the command above, you create a Runner that uses the [ruby:2.6](https://hub.docker.com/_/ruby) image and uses a [postgres](https://hub.docker.com/_/postgres) database.
|
||||||
|
|
||||||
To access the PostgreSQL database, connect to `host: postgres` as user `postgres` with no password.
|
To access the PostgreSQL database, connect to `host: postgres` as user `postgres` with no password.
|
||||||
|
|
|
@ -3645,7 +3645,7 @@ having their own custom `script` defined:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
.job_template: &job_definition # Hidden key that defines an anchor named 'job_definition'
|
.job_template: &job_definition # Hidden key that defines an anchor named 'job_definition'
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
services:
|
services:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
@ -3667,13 +3667,13 @@ given hash into the current one", and `*` includes the named anchor
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
.job_template:
|
.job_template:
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
services:
|
services:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
test1:
|
test1:
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
services:
|
services:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
@ -3681,7 +3681,7 @@ test1:
|
||||||
- test1 project
|
- test1 project
|
||||||
|
|
||||||
test2:
|
test2:
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
services:
|
services:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
|
|
@ -382,7 +382,7 @@ end
|
||||||
## String Freezing
|
## String Freezing
|
||||||
|
|
||||||
In recent Ruby versions calling `freeze` on a String leads to it being allocated
|
In recent Ruby versions calling `freeze` on a String leads to it being allocated
|
||||||
only once and re-used. For example, on Ruby 2.3 this will only allocate the
|
only once and re-used. For example, on Ruby 2.3 or later this will only allocate the
|
||||||
"foo" String once:
|
"foo" String once:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
|
|
@ -146,7 +146,7 @@ using environment variables.
|
||||||
| `PIP_INDEX_URL` | Base URL of Python Package Index (default `https://pypi.org/simple`). |
|
| `PIP_INDEX_URL` | Base URL of Python Package Index (default `https://pypi.org/simple`). |
|
||||||
| `PIP_EXTRA_INDEX_URL` | Array of [extra URLs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-extra-index-url) of package indexes to use in addition to `PIP_INDEX_URL`. Comma separated. |
|
| `PIP_EXTRA_INDEX_URL` | Array of [extra URLs](https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-extra-index-url) of package indexes to use in addition to `PIP_INDEX_URL`. Comma separated. |
|
||||||
| `PIP_REQUIREMENTS_FILE` | Pip requirements file to be scanned. |
|
| `PIP_REQUIREMENTS_FILE` | Pip requirements file to be scanned. |
|
||||||
| `MAVEN_CLI_OPTS` | List of command line arguments that will be passed to the maven analyzer during the project's build phase (see example for [using private repos](#using-private-maven-repos)). |
|
| `MAVEN_CLI_OPTS` | List of command line arguments that will be passed to `maven` by the analyzer. The default is `"-DskipTests --batch-mode"`. See an example for [using private repos](#using-private-maven-repos). |
|
||||||
| `BUNDLER_AUDIT_UPDATE_DISABLED` | Disable automatic updates for the `bundler-audit` analyzer (default: `"false"`). Useful if you're running Dependency Scanning in an offline, air-gapped environment.|
|
| `BUNDLER_AUDIT_UPDATE_DISABLED` | Disable automatic updates for the `bundler-audit` analyzer (default: `"false"`). Useful if you're running Dependency Scanning in an offline, air-gapped environment.|
|
||||||
|
|
||||||
### Using private Maven repos
|
### Using private Maven repos
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
last_updated: 2019-06-04
|
last_updated: 2020-01-06
|
||||||
type: reference, howto
|
type: reference, howto
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ first thing GitLab Runner will look for in your `.gitlab-ci.yml` is a
|
||||||
your container to run that script:
|
your container to run that script:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.7
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
script:
|
script:
|
||||||
|
@ -170,9 +170,9 @@ pages:
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, you're telling the Runner to pull this image, which
|
In this case, you're telling the Runner to pull this image, which
|
||||||
contains Ruby 2.3 as part of its file system. When you don't specify
|
contains Ruby 2.7 as part of its file system. When you don't specify
|
||||||
this image in your configuration, the Runner will use a default
|
this image in your configuration, the Runner will use a default
|
||||||
image, which is Ruby 2.1.
|
image, which is Ruby 2.6.
|
||||||
|
|
||||||
If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll
|
If your SSG needs [NodeJS](https://nodejs.org/) to build, you'll
|
||||||
need to specify which image you want to use, and this image should
|
need to specify which image you want to use, and this image should
|
||||||
|
@ -198,7 +198,7 @@ To do that, we need to add another line to our CI, telling the Runner
|
||||||
to only perform that _job_ called `pages` on the `master` branch `only`:
|
to only perform that _job_ called `pages` on the `master` branch `only`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.6
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
script:
|
script:
|
||||||
|
@ -221,7 +221,7 @@ and deploy. To specify which stage your _job_ is running,
|
||||||
simply add another line to your CI:
|
simply add another line to your CI:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.6
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
@ -244,7 +244,7 @@ let's add another task (_job_) to our CI, telling it to
|
||||||
test every push to other branches, `except` the `master` branch:
|
test every push to other branches, `except` the `master` branch:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.6
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
@ -294,7 +294,7 @@ every single _job_. In our example, notice that we run
|
||||||
We don't need to repeat it:
|
We don't need to repeat it:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.6
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- bundle install
|
- bundle install
|
||||||
|
@ -329,7 +329,7 @@ cache Jekyll dependencies in a `vendor` directory
|
||||||
when we run `bundle install`:
|
when we run `bundle install`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
image: ruby:2.3
|
image: ruby:2.6
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
type: reference
|
type: reference
|
||||||
last_updated: 2018-06-04
|
last_updated: 2020-01-06
|
||||||
---
|
---
|
||||||
|
|
||||||
# Exploring GitLab Pages
|
# Exploring GitLab Pages
|
||||||
|
@ -156,7 +156,7 @@ Below is a copy of `.gitlab-ci.yml` where the most significant line is the last
|
||||||
one, specifying to execute everything in the `pages` branch:
|
one, specifying to execute everything in the `pages` branch:
|
||||||
|
|
||||||
```
|
```
|
||||||
image: ruby:2.1
|
image: ruby:2.6
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
script:
|
script:
|
||||||
|
|
|
@ -178,6 +178,15 @@ module API
|
||||||
expose :only_protected_branches
|
expose :only_protected_branches
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ContainerExpirationPolicy < Grape::Entity
|
||||||
|
expose :cadence
|
||||||
|
expose :enabled
|
||||||
|
expose :keep_n
|
||||||
|
expose :older_than
|
||||||
|
expose :name_regex
|
||||||
|
expose :next_run_at
|
||||||
|
end
|
||||||
|
|
||||||
class ProjectImportStatus < ProjectIdentity
|
class ProjectImportStatus < ProjectIdentity
|
||||||
expose :import_status
|
expose :import_status
|
||||||
|
|
||||||
|
@ -276,6 +285,8 @@ module API
|
||||||
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
|
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
|
||||||
expose :resolve_outdated_diff_discussions
|
expose :resolve_outdated_diff_discussions
|
||||||
expose :container_registry_enabled
|
expose :container_registry_enabled
|
||||||
|
expose :container_expiration_policy, using: Entities::ContainerExpirationPolicy,
|
||||||
|
if: -> (project, _) { project.container_expiration_policy }
|
||||||
|
|
||||||
# Expose old field names with the new permissions methods to keep API compatible
|
# Expose old field names with the new permissions methods to keep API compatible
|
||||||
# TODO: remove in API v5, replaced by *_access_level
|
# TODO: remove in API v5, replaced by *_access_level
|
||||||
|
@ -341,6 +352,7 @@ module API
|
||||||
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
|
# MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555
|
||||||
super(projects_relation).preload(:group)
|
super(projects_relation).preload(:group)
|
||||||
.preload(:ci_cd_settings)
|
.preload(:ci_cd_settings)
|
||||||
|
.preload(:container_expiration_policy)
|
||||||
.preload(:auto_devops)
|
.preload(:auto_devops)
|
||||||
.preload(project_group_links: { group: :route },
|
.preload(project_group_links: { group: :route },
|
||||||
fork_network: :root_project,
|
fork_network: :root_project,
|
||||||
|
|
|
@ -32,6 +32,9 @@ module API
|
||||||
optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push'
|
optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push'
|
||||||
optional :remove_source_branch_after_merge, type: Boolean, desc: 'Remove the source branch by default after merge'
|
optional :remove_source_branch_after_merge, type: Boolean, desc: 'Remove the source branch by default after merge'
|
||||||
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
|
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
|
||||||
|
optional :container_expiration_policy_attributes, type: Hash do
|
||||||
|
use :optional_container_expiration_policy_params
|
||||||
|
end
|
||||||
optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
|
optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
|
||||||
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
|
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
|
||||||
optional :public_builds, type: Boolean, desc: 'Perform public builds'
|
optional :public_builds, type: Boolean, desc: 'Perform public builds'
|
||||||
|
@ -72,6 +75,14 @@ module API
|
||||||
params :optional_update_params_ee do
|
params :optional_update_params_ee do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
params :optional_container_expiration_policy_params do
|
||||||
|
optional :cadence, type: String, desc: 'Container expiration policy cadence for recurring job'
|
||||||
|
optional :keep_n, type: String, desc: 'Container expiration policy number of images to keep'
|
||||||
|
optional :older_than, type: String, desc: 'Container expiration policy remove images older than value'
|
||||||
|
optional :name_regex, type: String, desc: 'Container expiration policy regex for image removal'
|
||||||
|
optional :enabled, type: Boolean, desc: 'Flag indication if container expiration policy is enabled'
|
||||||
|
end
|
||||||
|
|
||||||
def self.update_params_at_least_one_of
|
def self.update_params_at_least_one_of
|
||||||
[
|
[
|
||||||
:auto_devops_enabled,
|
:auto_devops_enabled,
|
||||||
|
@ -84,6 +95,7 @@ module API
|
||||||
:ci_config_path,
|
:ci_config_path,
|
||||||
:ci_default_git_depth,
|
:ci_default_git_depth,
|
||||||
:container_registry_enabled,
|
:container_registry_enabled,
|
||||||
|
:container_expiration_policy_attributes,
|
||||||
:default_branch,
|
:default_branch,
|
||||||
:description,
|
:description,
|
||||||
:autoclose_referenced_issues,
|
:autoclose_referenced_issues,
|
||||||
|
|
|
@ -77,6 +77,7 @@ dependency_scanning:
|
||||||
services: []
|
services: []
|
||||||
except:
|
except:
|
||||||
variables:
|
variables:
|
||||||
|
- $DEPENDENCY_SCANNING_DISABLED
|
||||||
- $DS_DISABLE_DIND == 'false'
|
- $DS_DISABLE_DIND == 'false'
|
||||||
script:
|
script:
|
||||||
- /analyzer run
|
- /analyzer run
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Gitlab
|
||||||
|
module DatabaseImporters
|
||||||
|
module SelfMonitoring
|
||||||
|
module Project
|
||||||
|
class DeleteService < ::BaseService
|
||||||
|
include Stepable
|
||||||
|
include SelfMonitoring::Helpers
|
||||||
|
|
||||||
|
steps :validate_self_monitoring_project_exists,
|
||||||
|
:destroy_project_owner,
|
||||||
|
:delete_project_id
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
execute_steps
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_self_monitoring_project_exists(result)
|
||||||
|
unless project_created? || self_monitoring_project_id.present?
|
||||||
|
return error(_('Self monitoring project does not exist'))
|
||||||
|
end
|
||||||
|
|
||||||
|
success(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_project_owner(result)
|
||||||
|
return success(result) unless project_created?
|
||||||
|
|
||||||
|
if self_monitoring_project.owner.destroy
|
||||||
|
success(result)
|
||||||
|
else
|
||||||
|
log_error(self_monitoring_project.errors.full_messages)
|
||||||
|
error(_('Error deleting project. Check logs for error details.'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_project_id(result)
|
||||||
|
update_result = application_settings.update(
|
||||||
|
instance_administration_project_id: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
if update_result
|
||||||
|
success(result)
|
||||||
|
else
|
||||||
|
log_error("Could not delete self monitoring project ID, errors: %{errors}" % { errors: application_settings.errors.full_messages })
|
||||||
|
error(_('Could not delete project ID'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5121,6 +5121,9 @@ msgstr ""
|
||||||
msgid "Could not delete chat nickname %{chat_name}."
|
msgid "Could not delete chat nickname %{chat_name}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Could not delete project ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Could not fetch projects"
|
msgid "Could not fetch projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -7122,6 +7125,9 @@ msgstr ""
|
||||||
msgid "Error deleting %{issuableType}"
|
msgid "Error deleting %{issuableType}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Error deleting project. Check logs for error details."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Error details"
|
msgid "Error details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -16262,6 +16268,9 @@ msgstr ""
|
||||||
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
|
msgid "Selecting a GitLab user will add a link to the GitLab user in the descriptions of issues and comments (e.g. \"By <a href=\"#\">@johnsmith</a>\"). It will also associate and/or assign these issues and comments with the selected user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Self monitoring project does not exist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Self-monitoring is not enabled on this GitLab server, contact your administrator."
|
msgid "Self-monitoring is not enabled on this GitLab server, contact your administrator."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -124,14 +124,26 @@ describe GitlabSchema do
|
||||||
|
|
||||||
describe '.object_from_id' do
|
describe '.object_from_id' do
|
||||||
context 'for subclasses of `ApplicationRecord`' do
|
context 'for subclasses of `ApplicationRecord`' do
|
||||||
it 'returns the correct record' do
|
let_it_be(:user) { create(:user) }
|
||||||
user = create(:user)
|
|
||||||
|
|
||||||
|
it 'returns the correct record' do
|
||||||
result = described_class.object_from_id(user.to_global_id.to_s)
|
result = described_class.object_from_id(user.to_global_id.to_s)
|
||||||
|
|
||||||
expect(result.sync).to eq(user)
|
expect(result.sync).to eq(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns the correct record, of the expected type' do
|
||||||
|
result = described_class.object_from_id(user.to_global_id.to_s, expected_type: ::User)
|
||||||
|
|
||||||
|
expect(result.sync).to eq(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails if the type does not match' do
|
||||||
|
expect do
|
||||||
|
described_class.object_from_id(user.to_global_id.to_s, expected_type: ::Project)
|
||||||
|
end.to raise_error(Gitlab::Graphql::Errors::ArgumentError)
|
||||||
|
end
|
||||||
|
|
||||||
it 'batchloads the queries' do
|
it 'batchloads the queries' do
|
||||||
user1 = create(:user)
|
user1 = create(:user)
|
||||||
user2 = create(:user)
|
user2 = create(:user)
|
||||||
|
|
|
@ -7,7 +7,16 @@ describe GitlabSchema.types['Query'] do
|
||||||
expect(described_class.graphql_name).to eq('Query')
|
expect(described_class.graphql_name).to eq('Query')
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to have_graphql_fields(:project, :namespace, :group, :echo, :metadata, :current_user, :snippets) }
|
it do
|
||||||
|
is_expected.to have_graphql_fields(:project,
|
||||||
|
:namespace,
|
||||||
|
:group,
|
||||||
|
:echo,
|
||||||
|
:metadata,
|
||||||
|
:current_user,
|
||||||
|
:snippets
|
||||||
|
).at_least
|
||||||
|
end
|
||||||
|
|
||||||
describe 'namespace field' do
|
describe 'namespace field' do
|
||||||
subject { described_class.fields['namespace'] }
|
subject { described_class.fields['namespace'] }
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::DatabaseImporters::SelfMonitoring::Project::DeleteService do
|
||||||
|
describe '#execute' do
|
||||||
|
let(:result) { subject.execute }
|
||||||
|
let(:application_setting) { Gitlab::CurrentSettings.current_application_settings }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(ApplicationSetting).to receive(:current_without_cache) { application_setting }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when project does not exist' do
|
||||||
|
it 'returns error' do
|
||||||
|
expect(result).to eq(
|
||||||
|
status: :error,
|
||||||
|
message: 'Self monitoring project does not exist',
|
||||||
|
last_step: :validate_self_monitoring_project_exists
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with project destroyed but ID still present in application settings' do
|
||||||
|
before do
|
||||||
|
application_setting.instance_administration_project_id = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes project ID from application settings' do
|
||||||
|
subject.execute
|
||||||
|
|
||||||
|
expect(application_setting.instance_administration_project_id).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when self monitoring project exists' do
|
||||||
|
let(:group) { create(:group) }
|
||||||
|
let(:project) { create(:project, namespace: group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
application_setting.instance_administration_project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'destroys project' do
|
||||||
|
subject.execute
|
||||||
|
|
||||||
|
expect { project.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deletes project ID from application settings' do
|
||||||
|
subject.execute
|
||||||
|
|
||||||
|
expect(application_setting.instance_administration_project_id).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -57,7 +57,8 @@ describe Clusters::Applications::Jupyter do
|
||||||
it 'is initialized with 4 arguments' do
|
it 'is initialized with 4 arguments' do
|
||||||
expect(subject.name).to eq('jupyter')
|
expect(subject.name).to eq('jupyter')
|
||||||
expect(subject.chart).to eq('jupyter/jupyterhub')
|
expect(subject.chart).to eq('jupyter/jupyterhub')
|
||||||
expect(subject.version).to eq('0.9-174bbd5')
|
expect(subject.version).to eq('0.9.0-beta.2')
|
||||||
|
|
||||||
expect(subject).to be_rbac
|
expect(subject).to be_rbac
|
||||||
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
|
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
|
||||||
expect(subject.files).to eq(jupyter.files)
|
expect(subject.files).to eq(jupyter.files)
|
||||||
|
@ -75,7 +76,7 @@ describe Clusters::Applications::Jupyter do
|
||||||
let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') }
|
let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') }
|
||||||
|
|
||||||
it 'is initialized with the locked version' do
|
it 'is initialized with the locked version' do
|
||||||
expect(subject.version).to eq('0.9-174bbd5')
|
expect(subject.version).to eq('0.9.0-beta.2')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#code' do
|
describe 'CycleAnalytics#code' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
context 'with deployment' do
|
context 'with deployment' do
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
|
@ -24,8 +25,6 @@ describe 'CycleAnalytics#code' do
|
||||||
context.create_merge_request_closing_issue(context.user, context.project, data[:issue])
|
context.create_merge_request_closing_issue(context.user, context.project, data[:issue])
|
||||||
end]],
|
end]],
|
||||||
post_fn: -> (context, data) do
|
post_fn: -> (context, data) do
|
||||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
|
||||||
context.deploy_master(context.user, context.project)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context "when a regular merge request (that doesn't close the issue) is created" do
|
context "when a regular merge request (that doesn't close the issue) is created" do
|
||||||
|
@ -56,7 +55,6 @@ describe 'CycleAnalytics#code' do
|
||||||
context.create_merge_request_closing_issue(context.user, context.project, data[:issue])
|
context.create_merge_request_closing_issue(context.user, context.project, data[:issue])
|
||||||
end]],
|
end]],
|
||||||
post_fn: -> (context, data) do
|
post_fn: -> (context, data) do
|
||||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context "when a regular merge request (that doesn't close the issue) is created" do
|
context "when a regular merge request (that doesn't close the issue) is created" do
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe CycleAnalytics::GroupLevel do
|
describe CycleAnalytics::GroupLevel do
|
||||||
let(:group) { create(:group)}
|
let_it_be(:group) { create(:group)}
|
||||||
let(:project) { create(:project, :repository, namespace: group) }
|
let_it_be(:project) { create(:project, :repository, namespace: group) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||||
let(:milestone) { create(:milestone, project: project) }
|
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||||
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
|
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
|
||||||
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
|
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#issue' do
|
describe 'CycleAnalytics#issue' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
phase: :issue,
|
phase: :issue,
|
||||||
|
@ -28,10 +29,6 @@ describe 'CycleAnalytics#issue' do
|
||||||
end
|
end
|
||||||
end]],
|
end]],
|
||||||
post_fn: -> (context, data) do
|
post_fn: -> (context, data) do
|
||||||
if data[:issue].persisted?
|
|
||||||
context.create_merge_request_closing_issue(context.user, context.project, data[:issue].reload)
|
|
||||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context "when a regular label (instead of a list label) is added to the issue" do
|
context "when a regular label (instead of a list label) is added to the issue" do
|
||||||
|
|
|
@ -5,17 +5,18 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#plan' do
|
describe 'CycleAnalytics#plan' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
phase: :plan,
|
phase: :plan,
|
||||||
data_fn: -> (context) do
|
data_fn: -> (context) do
|
||||||
{
|
{
|
||||||
issue: context.create(:issue, project: context.project),
|
issue: context.build(:issue, project: context.project),
|
||||||
branch_name: context.generate(:branch)
|
branch_name: context.generate(:branch)
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
|
@ -32,8 +33,6 @@ describe 'CycleAnalytics#plan' do
|
||||||
context.create_commit_referencing_issue(data[:issue], branch_name: data[:branch_name])
|
context.create_commit_referencing_issue(data[:issue], branch_name: data[:branch_name])
|
||||||
end]],
|
end]],
|
||||||
post_fn: -> (context, data) do
|
post_fn: -> (context, data) do
|
||||||
context.create_merge_request_closing_issue(context.user, context.project, data[:issue], source_branch: data[:branch_name])
|
|
||||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context "when a regular label (instead of a list label) is added to the issue" do
|
context "when a regular label (instead of a list label) is added to the issue" do
|
||||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#production' do
|
describe 'CycleAnalytics#production' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
phase: :production,
|
phase: :production,
|
||||||
|
@ -24,13 +25,7 @@ describe 'CycleAnalytics#production' do
|
||||||
["production deploy happens after merge request is merged (along with other changes)",
|
["production deploy happens after merge request is merged (along with other changes)",
|
||||||
lambda do |context, data|
|
lambda do |context, data|
|
||||||
# Make other changes on master
|
# Make other changes on master
|
||||||
sha = context.project.repository.create_file(
|
context.project.repository.commit("sha_that_does_not_matter")
|
||||||
context.user,
|
|
||||||
context.generate(:branch),
|
|
||||||
'content',
|
|
||||||
message: 'commit message',
|
|
||||||
branch_name: 'master')
|
|
||||||
context.project.repository.commit(sha)
|
|
||||||
|
|
||||||
context.deploy_master(context.user, context.project)
|
context.deploy_master(context.user, context.project)
|
||||||
end]])
|
end]])
|
||||||
|
@ -47,7 +42,7 @@ describe 'CycleAnalytics#production' do
|
||||||
|
|
||||||
context "when the deployment happens to a non-production environment" do
|
context "when the deployment happens to a non-production environment" do
|
||||||
it "returns nil" do
|
it "returns nil" do
|
||||||
issue = create(:issue, project: project)
|
issue = build(:issue, project: project)
|
||||||
merge_request = create_merge_request_closing_issue(user, project, issue)
|
merge_request = create_merge_request_closing_issue(user, project, issue)
|
||||||
MergeRequests::MergeService.new(project, user).execute(merge_request)
|
MergeRequests::MergeService.new(project, user).execute(merge_request)
|
||||||
deploy_master(user, project, environment: 'staging')
|
deploy_master(user, project, environment: 'staging')
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe CycleAnalytics::ProjectLevel do
|
describe CycleAnalytics::ProjectLevel do
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
let_it_be(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||||
let(:milestone) { create(:milestone, project: project) }
|
let_it_be(:milestone) { create(:milestone, project: project) }
|
||||||
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
|
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
|
||||||
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
|
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#review' do
|
describe 'CycleAnalytics#review' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#staging' do
|
describe 'CycleAnalytics#staging' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
phase: :staging,
|
phase: :staging,
|
||||||
|
@ -28,14 +29,7 @@ describe 'CycleAnalytics#staging' do
|
||||||
["production deploy happens after merge request is merged (along with other changes)",
|
["production deploy happens after merge request is merged (along with other changes)",
|
||||||
lambda do |context, data|
|
lambda do |context, data|
|
||||||
# Make other changes on master
|
# Make other changes on master
|
||||||
sha = context.project.repository.create_file(
|
context.project.repository.commit("this_sha_apparently_does_not_matter")
|
||||||
context.user,
|
|
||||||
context.generate(:branch),
|
|
||||||
'content',
|
|
||||||
message: 'commit message',
|
|
||||||
branch_name: 'master')
|
|
||||||
context.project.repository.commit(sha)
|
|
||||||
|
|
||||||
context.deploy_master(context.user, context.project)
|
context.deploy_master(context.user, context.project)
|
||||||
end]])
|
end]])
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,19 @@ require 'spec_helper'
|
||||||
describe 'CycleAnalytics#test' do
|
describe 'CycleAnalytics#test' do
|
||||||
extend CycleAnalyticsHelpers::TestGeneration
|
extend CycleAnalyticsHelpers::TestGeneration
|
||||||
|
|
||||||
let(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
let(:from_date) { 10.days.ago }
|
let_it_be(:from_date) { 10.days.ago }
|
||||||
let(:user) { create(:user, :admin) }
|
let_it_be(:user) { create(:user, :admin) }
|
||||||
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
|
let_it_be(:project_level) { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||||
|
let!(:merge_request) { create_merge_request_closing_issue(user, project, issue) }
|
||||||
|
|
||||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
subject { project_level }
|
||||||
|
|
||||||
generate_cycle_analytics_spec(
|
generate_cycle_analytics_spec(
|
||||||
phase: :test,
|
phase: :test,
|
||||||
data_fn: lambda do |context|
|
data_fn: lambda do |context|
|
||||||
issue = context.create(:issue, project: context.project)
|
issue = context.issue
|
||||||
merge_request = context.create_merge_request_closing_issue(context.user, context.project, issue)
|
merge_request = context.create_merge_request_closing_issue(context.user, context.project, issue)
|
||||||
pipeline = context.create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, project: context.project, head_pipeline_of: merge_request)
|
pipeline = context.create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, project: context.project, head_pipeline_of: merge_request)
|
||||||
{ pipeline: pipeline, issue: issue }
|
{ pipeline: pipeline, issue: issue }
|
||||||
|
@ -22,20 +25,15 @@ describe 'CycleAnalytics#test' do
|
||||||
start_time_conditions: [["pipeline is started", -> (context, data) { data[:pipeline].run! }]],
|
start_time_conditions: [["pipeline is started", -> (context, data) { data[:pipeline].run! }]],
|
||||||
end_time_conditions: [["pipeline is finished", -> (context, data) { data[:pipeline].succeed! }]],
|
end_time_conditions: [["pipeline is finished", -> (context, data) { data[:pipeline].succeed! }]],
|
||||||
post_fn: -> (context, data) do
|
post_fn: -> (context, data) do
|
||||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
context "when the pipeline is for a regular merge request (that doesn't close an issue)" do
|
context "when the pipeline is for a regular merge request (that doesn't close an issue)" do
|
||||||
it "returns nil" do
|
it "returns nil" do
|
||||||
issue = create(:issue, project: project)
|
|
||||||
merge_request = create_merge_request_closing_issue(user, project, issue)
|
|
||||||
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
||||||
|
|
||||||
pipeline.run!
|
pipeline.run!
|
||||||
pipeline.succeed!
|
pipeline.succeed!
|
||||||
|
|
||||||
merge_merge_requests_closing_issue(user, project, issue)
|
|
||||||
|
|
||||||
expect(subject[:test].project_median).to be_nil
|
expect(subject[:test].project_median).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,30 +51,22 @@ describe 'CycleAnalytics#test' do
|
||||||
|
|
||||||
context "when the pipeline is dropped (failed)" do
|
context "when the pipeline is dropped (failed)" do
|
||||||
it "returns nil" do
|
it "returns nil" do
|
||||||
issue = create(:issue, project: project)
|
|
||||||
merge_request = create_merge_request_closing_issue(user, project, issue)
|
|
||||||
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
||||||
|
|
||||||
pipeline.run!
|
pipeline.run!
|
||||||
pipeline.drop!
|
pipeline.drop!
|
||||||
|
|
||||||
merge_merge_requests_closing_issue(user, project, issue)
|
|
||||||
|
|
||||||
expect(subject[:test].project_median).to be_nil
|
expect(subject[:test].project_median).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the pipeline is cancelled" do
|
context "when the pipeline is cancelled" do
|
||||||
it "returns nil" do
|
it "returns nil" do
|
||||||
issue = create(:issue, project: project)
|
|
||||||
merge_request = create_merge_request_closing_issue(user, project, issue)
|
|
||||||
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
pipeline = create(:ci_pipeline, ref: "refs/heads/#{merge_request.source_branch}", sha: merge_request.diff_head_sha)
|
||||||
|
|
||||||
pipeline.run!
|
pipeline.run!
|
||||||
pipeline.cancel!
|
pipeline.cancel!
|
||||||
|
|
||||||
merge_merge_requests_closing_issue(user, project, issue)
|
|
||||||
|
|
||||||
expect(subject[:test].project_median).to be_nil
|
expect(subject[:test].project_median).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,4 +26,34 @@ describe ExternalWikiService do
|
||||||
it { is_expected.not_to validate_presence_of(:external_wiki_url) }
|
it { is_expected.not_to validate_presence_of(:external_wiki_url) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'test' do
|
||||||
|
before do
|
||||||
|
subject.properties['external_wiki_url'] = url
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:url) { 'http://foo' }
|
||||||
|
let(:data) { nil }
|
||||||
|
let(:result) { subject.test(data) }
|
||||||
|
|
||||||
|
context 'the URL is not reachable' do
|
||||||
|
before do
|
||||||
|
WebMock.stub_request(:get, url).to_return(status: 404, body: 'not a page')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not successful' do
|
||||||
|
expect(result[:success]).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'the URL is reachable' do
|
||||||
|
before do
|
||||||
|
WebMock.stub_request(:get, url).to_return(status: 200, body: 'foo')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful' do
|
||||||
|
expect(result[:success]).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2251,6 +2251,22 @@ describe API::Projects do
|
||||||
put api("/projects/#{project3.id}", user4), params: project_param
|
put api("/projects/#{project3.id}", user4), params: project_param
|
||||||
expect(response).to have_gitlab_http_status(403)
|
expect(response).to have_gitlab_http_status(403)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates container_expiration_policy' do
|
||||||
|
project_param = {
|
||||||
|
container_expiration_policy_attributes: {
|
||||||
|
cadence: '1month',
|
||||||
|
keep_n: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put api("/projects/#{project3.id}", user4), params: project_param
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
|
||||||
|
expect(json_response['container_expiration_policy']['cadence']).to eq('1month')
|
||||||
|
expect(json_response['container_expiration_policy']['keep_n']).to eq(1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when authenticated as project developer' do
|
context 'when authenticated as project developer' do
|
||||||
|
|
|
@ -27,6 +27,8 @@ module CycleAnalyticsHelpers
|
||||||
|
|
||||||
scenarios = combinations_of_start_time_conditions.product(combinations_of_end_time_conditions)
|
scenarios = combinations_of_start_time_conditions.product(combinations_of_end_time_conditions)
|
||||||
scenarios.each do |start_time_conditions, end_time_conditions|
|
scenarios.each do |start_time_conditions, end_time_conditions|
|
||||||
|
let_it_be(:other_project) { create(:project, :repository) }
|
||||||
|
|
||||||
context "start condition: #{start_time_conditions.map(&:first).to_sentence}" do
|
context "start condition: #{start_time_conditions.map(&:first).to_sentence}" do
|
||||||
context "end condition: #{end_time_conditions.map(&:first).to_sentence}" do
|
context "end condition: #{end_time_conditions.map(&:first).to_sentence}" do
|
||||||
it "finds the median of available durations between the two conditions", :sidekiq_might_not_need_inline do
|
it "finds the median of available durations between the two conditions", :sidekiq_might_not_need_inline do
|
||||||
|
@ -56,8 +58,6 @@ module CycleAnalyticsHelpers
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the data belongs to another project" do
|
context "when the data belongs to another project" do
|
||||||
let(:other_project) { create(:project, :repository) }
|
|
||||||
|
|
||||||
it "returns nil" do
|
it "returns nil" do
|
||||||
# Use a stub to "trick" the data/condition functions
|
# Use a stub to "trick" the data/condition functions
|
||||||
# into using another project. This saves us from having to
|
# into using another project. This saves us from having to
|
||||||
|
|
|
@ -105,12 +105,15 @@ module GraphqlHelpers
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_graphql_field(name, attributes = {}, fields = nil)
|
def query_graphql_field(name, attributes = {}, fields = nil)
|
||||||
fields ||= all_graphql_fields_for(name.classify)
|
field_params = if attributes.present?
|
||||||
attributes = attributes_to_graphql(attributes)
|
"(#{attributes_to_graphql(attributes)})"
|
||||||
attributes = "(#{attributes})" if attributes.present?
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
<<~QUERY
|
<<~QUERY
|
||||||
#{name}#{attributes}
|
#{GraphqlHelpers.fieldnamerize(name.to_s)}#{field_params}
|
||||||
#{wrap_fields(fields)}
|
#{wrap_fields(fields || all_graphql_fields_for(name.to_s.classify))}
|
||||||
QUERY
|
QUERY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -301,6 +304,17 @@ module GraphqlHelpers
|
||||||
def global_id_of(model)
|
def global_id_of(model)
|
||||||
model.to_global_id.to_s
|
model.to_global_id.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def missing_required_argument(path, argument)
|
||||||
|
a_hash_including(
|
||||||
|
'path' => ['query'].concat(path),
|
||||||
|
'extensions' => a_hash_including('code' => 'missingRequiredArguments', 'arguments' => argument.to_s)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_graphql_error(path, msg)
|
||||||
|
a_hash_including('path' => path, 'message' => msg)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This warms our schema, doing this as part of loading the helpers to avoid
|
# This warms our schema, doing this as part of loading the helpers to avoid
|
||||||
|
|
|
@ -11,8 +11,22 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
|
||||||
Array.wrap(expected).map { |name| GraphqlHelpers.fieldnamerize(name) }
|
Array.wrap(expected).map { |name| GraphqlHelpers.fieldnamerize(name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@allow_extra = false
|
||||||
|
|
||||||
|
chain :only do
|
||||||
|
@allow_extra = false
|
||||||
|
end
|
||||||
|
|
||||||
|
chain :at_least do
|
||||||
|
@allow_extra = true
|
||||||
|
end
|
||||||
|
|
||||||
match do |kls|
|
match do |kls|
|
||||||
expect(kls.fields.keys).to contain_exactly(*expected_field_names)
|
if @allow_extra
|
||||||
|
expect(kls.fields.keys).to include(*expected_field_names)
|
||||||
|
else
|
||||||
|
expect(kls.fields.keys).to contain_exactly(*expected_field_names)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
failure_message do |kls|
|
failure_message do |kls|
|
||||||
|
@ -22,7 +36,7 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
|
||||||
message = []
|
message = []
|
||||||
|
|
||||||
message << "is missing fields: <#{missing.inspect}>" if missing.any?
|
message << "is missing fields: <#{missing.inspect}>" if missing.any?
|
||||||
message << "contained unexpected fields: <#{extra.inspect}>" if extra.any?
|
message << "contained unexpected fields: <#{extra.inspect}>" if extra.any? && !@allow_extra
|
||||||
|
|
||||||
message.join("\n")
|
message.join("\n")
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
# Shared example for legal queries that are expected to return nil.
|
||||||
|
# Requires the following let bindings to be defined:
|
||||||
|
# - post_query: action to send the query
|
||||||
|
# - path: array of keys from query root to the result
|
||||||
|
shared_examples 'a failure to find anything' do
|
||||||
|
it 'finds nothing' do
|
||||||
|
post_query
|
||||||
|
|
||||||
|
data = graphql_data.dig(*path)
|
||||||
|
|
||||||
|
expect(data).to be_nil
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue