Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
73391dcc36
commit
e0b84f4ba4
|
@ -86,4 +86,4 @@ jsdoc/
|
|||
.projections.json
|
||||
/qa/.rakeTasks
|
||||
webpack-dev-server.json
|
||||
.nvimrc
|
||||
/.nvimrc
|
||||
|
|
|
@ -3,11 +3,13 @@ import { mapGetters } from 'vuex';
|
|||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import store from '~/pipelines/stores/test_reports';
|
||||
import { __ } from '~/locale';
|
||||
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||
|
||||
export default {
|
||||
name: 'TestsSuiteTable',
|
||||
components: {
|
||||
Icon,
|
||||
SmartVirtualList,
|
||||
},
|
||||
store,
|
||||
props: {
|
||||
|
@ -23,6 +25,8 @@ export default {
|
|||
return this.getSuiteTests.length > 0;
|
||||
},
|
||||
},
|
||||
maxShownRows: 30,
|
||||
typicalRowHeight: 75,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -34,7 +38,7 @@ export default {
|
|||
</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="rowheader" class="table-section section-20">
|
||||
{{ __('Class') }}
|
||||
|
@ -53,52 +57,58 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(testCase, index) in getSuiteTests"
|
||||
:key="index"
|
||||
class="gl-responsive-table-row rounded align-items-md-start mt-sm-3 js-case-row"
|
||||
<smart-virtual-list
|
||||
:length="getSuiteTests.length"
|
||||
:remain="$options.maxShownRows"
|
||||
:size="$options.typicalRowHeight"
|
||||
>
|
||||
<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">{{ testCase.classname }}</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(testCase, index) in getSuiteTests"
|
||||
:key="index"
|
||||
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 role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
||||
<div class="table-mobile-content">{{ testCase.name }}</div>
|
||||
</div>
|
||||
<div class="table-section section-20 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
|
||||
<div class="table-mobile-content">{{ testCase.name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||
<div class="table-mobile-content text-center">
|
||||
<div
|
||||
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||
:class="`ci-status-icon-${testCase.status}`"
|
||||
>
|
||||
<icon :size="24" :name="testCase.icon" />
|
||||
<div class="table-section section-10 section-wrap">
|
||||
<div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
|
||||
<div class="table-mobile-content text-center">
|
||||
<div
|
||||
class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
|
||||
:class="`ci-status-icon-${testCase.status}`"
|
||||
>
|
||||
<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 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>
|
||||
</smart-virtual-list>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
import { mapGetters } from 'vuex';
|
||||
import { s__ } from '~/locale';
|
||||
import store from '~/pipelines/stores/test_reports';
|
||||
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
|
||||
|
||||
export default {
|
||||
name: 'TestsSummaryTable',
|
||||
components: {
|
||||
SmartVirtualList,
|
||||
},
|
||||
store,
|
||||
props: {
|
||||
heading: {
|
||||
|
@ -24,6 +28,8 @@ export default {
|
|||
this.$emit('row-click', suite);
|
||||
},
|
||||
},
|
||||
maxShownRows: 20,
|
||||
typicalRowHeight: 55,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -35,7 +41,7 @@ export default {
|
|||
</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="rowheader" class="table-section section-25 pl-3">
|
||||
{{ __('Suite') }}
|
||||
|
@ -60,66 +66,72 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(testSuite, index) in getTestSuites"
|
||||
: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)"
|
||||
<smart-virtual-list
|
||||
:length="getTestSuites.length"
|
||||
:remain="$options.maxShownRows"
|
||||
:size="$options.typicalRowHeight"
|
||||
>
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Suite') }}
|
||||
<div
|
||||
v-for="(testSuite, index) in getTestSuites"
|
||||
: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 class="table-mobile-content underline cgray pl-3">
|
||||
{{ testSuite.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Duration') }}
|
||||
<div class="table-section section-25">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Duration') }}
|
||||
</div>
|
||||
<div class="table-mobile-content text-md-left">
|
||||
{{ testSuite.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-mobile-content text-md-left">
|
||||
{{ testSuite.formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Failed') }}
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Failed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.failed_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Errors') }}
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Errors') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.error_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Skipped') }}
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Skipped') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Passed') }}
|
||||
<div class="table-section section-10 text-center">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Passed') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.success_count }}</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10 text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Total') }}
|
||||
<div class="table-section section-10 text-right pr-md-3">
|
||||
<div role="rowheader" class="table-mobile-header font-weight-bold">
|
||||
{{ __('Total') }}
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||
</div>
|
||||
<div class="table-mobile-content">{{ testSuite.total_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</smart-virtual-list>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
|
|
|
@ -57,20 +57,24 @@ class GitlabSchema < GraphQL::Schema
|
|||
object.to_global_id
|
||||
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 = {})
|
||||
expected_type = ctx[:expected_type]
|
||||
gid = GlobalID.parse(global_id)
|
||||
gid = parse_gid(global_id, ctx)
|
||||
|
||||
unless gid
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, "#{global_id} is not a valid GitLab id."
|
||||
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_by_gid(gid)
|
||||
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
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(gid.model_class, gid.model_id).find
|
||||
elsif gid.model_class.respond_to?(:lazy_find)
|
||||
|
@ -80,6 +84,38 @@ class GitlabSchema < GraphQL::Schema
|
|||
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
|
||||
|
||||
def max_query_complexity(ctx)
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'securerandom'
|
|||
module Clusters
|
||||
module Applications
|
||||
class Jupyter < ApplicationRecord
|
||||
VERSION = '0.9-174bbd5'
|
||||
VERSION = '0.9.0-beta.2'
|
||||
|
||||
self.table_name = 'clusters_applications_jupyter'
|
||||
|
||||
|
|
|
@ -19,15 +19,20 @@ class ExternalWikiService < Service
|
|||
|
||||
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
|
||||
|
||||
def execute(_data)
|
||||
@response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true) rescue nil
|
||||
if @response != 200
|
||||
nil
|
||||
end
|
||||
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
|
||||
response.body if response.code == 200
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
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
|
||||
graph TB
|
||||
GitLab --> Praefect;
|
||||
Praefect --> Gitaly-1;
|
||||
Praefect --> Gitaly-2;
|
||||
Praefect --> Gitaly-3;
|
||||
Praefect --- PostgreSQL;
|
||||
Praefect --> Gitaly1;
|
||||
Praefect --> Gitaly2;
|
||||
Praefect --> Gitaly3;
|
||||
```
|
||||
|
||||
Where `GitLab` is the collection of clients that can request Git operations.
|
||||
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`.
|
||||
|
||||
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.
|
||||
In the example below we will use a separate server, but the optimal configuration
|
||||
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
|
||||
access internal nodes of the Praefect cluster directly; that could
|
||||
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
|
||||
|
||||
|
@ -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).
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
[runners.docker]
|
||||
image = "ruby:2.1"
|
||||
image = "ruby:2.6"
|
||||
privileged = false
|
||||
volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
|
||||
```
|
||||
|
|
|
@ -529,9 +529,9 @@ type CreateSnippetPayload {
|
|||
snippet: Snippet
|
||||
}
|
||||
|
||||
type Design implements Noteable {
|
||||
type Design implements DesignFields & Noteable {
|
||||
"""
|
||||
Diff refs of the design
|
||||
The diff refs for this design
|
||||
"""
|
||||
diffRefs: DiffRefs!
|
||||
|
||||
|
@ -561,33 +561,32 @@ type Design implements Noteable {
|
|||
): DiscussionConnection!
|
||||
|
||||
"""
|
||||
Type of change made to the design at the version specified by the `atVersion`
|
||||
argument if supplied. Defaults to the latest version
|
||||
How this design was changed in the current version
|
||||
"""
|
||||
event: DesignVersionEvent!
|
||||
|
||||
"""
|
||||
Filename of the design file
|
||||
The filename of the design
|
||||
"""
|
||||
filename: String!
|
||||
|
||||
"""
|
||||
Full path of the design file
|
||||
The full path to the design file
|
||||
"""
|
||||
fullPath: String!
|
||||
|
||||
"""
|
||||
ID of the design
|
||||
The ID of this design
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
Image of the design
|
||||
The URL of the image
|
||||
"""
|
||||
image: String!
|
||||
|
||||
"""
|
||||
Issue associated with the design
|
||||
The issue the design belongs to
|
||||
"""
|
||||
issue: Issue!
|
||||
|
||||
|
@ -617,17 +616,17 @@ type Design implements Noteable {
|
|||
): NoteConnection!
|
||||
|
||||
"""
|
||||
Total count of user-created notes for the design
|
||||
The total count of user-created notes for this design
|
||||
"""
|
||||
notesCount: Int!
|
||||
|
||||
"""
|
||||
Project associated with the design
|
||||
The project the design belongs to
|
||||
"""
|
||||
project: Project!
|
||||
|
||||
"""
|
||||
All versions related to the design, ordered newest first
|
||||
All versions related to this design ordered newest first
|
||||
"""
|
||||
versions(
|
||||
"""
|
||||
|
@ -765,6 +764,53 @@ type DesignEdge {
|
|||
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
|
||||
"""
|
||||
|
|
|
@ -10350,7 +10350,7 @@
|
|||
"fields": [
|
||||
{
|
||||
"name": "diffRefs",
|
||||
"description": "Diff refs of the design",
|
||||
"description": "The diff refs for this design",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10425,7 +10425,7 @@
|
|||
},
|
||||
{
|
||||
"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": [
|
||||
|
||||
],
|
||||
|
@ -10443,7 +10443,7 @@
|
|||
},
|
||||
{
|
||||
"name": "filename",
|
||||
"description": "Filename of the design file",
|
||||
"description": "The filename of the design",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10461,7 +10461,7 @@
|
|||
},
|
||||
{
|
||||
"name": "fullPath",
|
||||
"description": "Full path of the design file",
|
||||
"description": "The full path to the design file",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10479,7 +10479,7 @@
|
|||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID of the design",
|
||||
"description": "The ID of this design",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10497,7 +10497,7 @@
|
|||
},
|
||||
{
|
||||
"name": "image",
|
||||
"description": "Image of the design",
|
||||
"description": "The URL of the image",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10515,7 +10515,7 @@
|
|||
},
|
||||
{
|
||||
"name": "issue",
|
||||
"description": "Issue associated with the design",
|
||||
"description": "The issue the design belongs to",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10590,7 +10590,7 @@
|
|||
},
|
||||
{
|
||||
"name": "notesCount",
|
||||
"description": "Total count of user-created notes for the design",
|
||||
"description": "The total count of user-created notes for this design",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10608,7 +10608,7 @@
|
|||
},
|
||||
{
|
||||
"name": "project",
|
||||
"description": "Project associated with the design",
|
||||
"description": "The project the design belongs to",
|
||||
"args": [
|
||||
|
||||
],
|
||||
|
@ -10626,7 +10626,7 @@
|
|||
},
|
||||
{
|
||||
"name": "versions",
|
||||
"description": "All versions related to the design, ordered newest first",
|
||||
"description": "All versions related to this design ordered newest first",
|
||||
"args": [
|
||||
{
|
||||
"name": "after",
|
||||
|
@ -10688,11 +10688,195 @@
|
|||
"kind": "INTERFACE",
|
||||
"name": "Noteable",
|
||||
"ofType": null
|
||||
},
|
||||
{
|
||||
"kind": "INTERFACE",
|
||||
"name": "DesignFields",
|
||||
"ofType": null
|
||||
}
|
||||
],
|
||||
"enumValues": 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",
|
||||
"name": "DesignVersionEvent",
|
||||
|
|
|
@ -104,15 +104,15 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
|||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `id` | ID! | ID of the design |
|
||||
| `project` | Project! | Project associated with the design |
|
||||
| `issue` | Issue! | Issue associated with the design |
|
||||
| `notesCount` | Int! | Total count of user-created notes for the design |
|
||||
| `filename` | String! | Filename of the design file |
|
||||
| `fullPath` | String! | Full path of the design file |
|
||||
| `event` | DesignVersionEvent! | Type of change made to the design at the version specified by the `atVersion` argument if supplied. Defaults to the latest version |
|
||||
| `image` | String! | Image of the design |
|
||||
| `diffRefs` | DiffRefs! | Diff refs of the design |
|
||||
| `id` | ID! | The ID of this design |
|
||||
| `project` | Project! | The project the design belongs to |
|
||||
| `issue` | Issue! | The issue the design belongs to |
|
||||
| `filename` | String! | The filename of the design |
|
||||
| `fullPath` | String! | The full path to the design file |
|
||||
| `image` | String! | The URL of the image |
|
||||
| `diffRefs` | DiffRefs! | The diff refs for this design |
|
||||
| `event` | DesignVersionEvent! | How this design was changed in the current version |
|
||||
| `notesCount` | Int! | The total count of user-created notes for this design |
|
||||
|
||||
### DesignCollection
|
||||
|
||||
|
|
|
@ -761,6 +761,14 @@ GET /projects/:id
|
|||
"snippets_enabled": false,
|
||||
"resolve_outdated_diff_discussions": 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",
|
||||
"last_activity_at": "2013-09-30T13:46:02Z",
|
||||
"creator_id": 3,
|
||||
|
@ -986,6 +994,7 @@ POST /projects
|
|||
| `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 |
|
||||
| `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 |
|
||||
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
||||
| `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` |
|
||||
| `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_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 |
|
||||
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
|
||||
| `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 \
|
||||
--url "https://gitlab.example.com/" \
|
||||
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
||||
--description "docker-ruby-2.1" \
|
||||
--description "docker-ruby:2.6" \
|
||||
--executor "docker" \
|
||||
--docker-image ruby:2.1 \
|
||||
--docker-image ruby:2.6 \
|
||||
--docker-services postgres: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
|
||||
accessible during the build process.
|
||||
|
||||
|
@ -194,7 +194,7 @@ services that you want to use during build time:
|
|||
|
||||
```yaml
|
||||
default:
|
||||
image: ruby:2.2
|
||||
image: ruby:2.6
|
||||
|
||||
services:
|
||||
- postgres:9.3
|
||||
|
@ -214,15 +214,15 @@ default:
|
|||
before_script:
|
||||
- bundle install
|
||||
|
||||
test:2.1:
|
||||
image: ruby:2.1
|
||||
test:2.6:
|
||||
image: ruby:2.6
|
||||
services:
|
||||
- postgres:9.3
|
||||
script:
|
||||
- bundle exec rake spec
|
||||
|
||||
test:2.2:
|
||||
image: ruby:2.2
|
||||
test:2.7:
|
||||
image: ruby:2.7
|
||||
services:
|
||||
- postgres:9.4
|
||||
script:
|
||||
|
@ -235,7 +235,7 @@ for `image` and `services`:
|
|||
```yaml
|
||||
default:
|
||||
image:
|
||||
name: ruby:2.2
|
||||
name: ruby:2.6
|
||||
entrypoint: ["/bin/bash"]
|
||||
|
||||
services:
|
||||
|
@ -277,7 +277,7 @@ services:
|
|||
command: ["postgres"]
|
||||
|
||||
image:
|
||||
name: ruby:2.2
|
||||
name: ruby:2.6
|
||||
entrypoint: ["/bin/bash"]
|
||||
|
||||
before_script:
|
||||
|
@ -773,7 +773,7 @@ time.
|
|||
|
||||
1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`.
|
||||
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. Start build container and send job script to the container.
|
||||
1. Run job script.
|
||||
|
@ -818,11 +818,11 @@ Finally, create a build container by executing the `build_script` file we
|
|||
created earlier:
|
||||
|
||||
```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 `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
|
||||
`build_script` in the `build` container.
|
||||
|
||||
|
|
|
@ -71,12 +71,12 @@ gitlab-runner register \
|
|||
--non-interactive \
|
||||
--url "https://gitlab.com/" \
|
||||
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
||||
--description "ruby-2.2" \
|
||||
--description "ruby:2.6" \
|
||||
--executor "docker" \
|
||||
--docker-image ruby:2.2 \
|
||||
--docker-image ruby:2.6 \
|
||||
--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.
|
||||
|
|
|
@ -3645,7 +3645,7 @@ having their own custom `script` defined:
|
|||
|
||||
```yaml
|
||||
.job_template: &job_definition # Hidden key that defines an anchor named 'job_definition'
|
||||
image: ruby:2.1
|
||||
image: ruby:2.6
|
||||
services:
|
||||
- postgres
|
||||
- redis
|
||||
|
@ -3667,13 +3667,13 @@ given hash into the current one", and `*` includes the named anchor
|
|||
|
||||
```yaml
|
||||
.job_template:
|
||||
image: ruby:2.1
|
||||
image: ruby:2.6
|
||||
services:
|
||||
- postgres
|
||||
- redis
|
||||
|
||||
test1:
|
||||
image: ruby:2.1
|
||||
image: ruby:2.6
|
||||
services:
|
||||
- postgres
|
||||
- redis
|
||||
|
@ -3681,7 +3681,7 @@ test1:
|
|||
- test1 project
|
||||
|
||||
test2:
|
||||
image: ruby:2.1
|
||||
image: ruby:2.6
|
||||
services:
|
||||
- postgres
|
||||
- redis
|
||||
|
|
|
@ -382,7 +382,7 @@ end
|
|||
## String Freezing
|
||||
|
||||
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:
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -146,7 +146,7 @@ using environment variables.
|
|||
| `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_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.|
|
||||
|
||||
### Using private Maven repos
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
last_updated: 2019-06-04
|
||||
last_updated: 2020-01-06
|
||||
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:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.7
|
||||
|
||||
pages:
|
||||
script:
|
||||
|
@ -170,9 +170,9 @@ pages:
|
|||
```
|
||||
|
||||
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
|
||||
image, which is Ruby 2.1.
|
||||
image, which is Ruby 2.6.
|
||||
|
||||
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
|
||||
|
@ -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`:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.6
|
||||
|
||||
pages:
|
||||
script:
|
||||
|
@ -221,7 +221,7 @@ and deploy. To specify which stage your _job_ is running,
|
|||
simply add another line to your CI:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.6
|
||||
|
||||
pages:
|
||||
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:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.6
|
||||
|
||||
pages:
|
||||
stage: deploy
|
||||
|
@ -294,7 +294,7 @@ every single _job_. In our example, notice that we run
|
|||
We don't need to repeat it:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.6
|
||||
|
||||
before_script:
|
||||
- bundle install
|
||||
|
@ -329,7 +329,7 @@ cache Jekyll dependencies in a `vendor` directory
|
|||
when we run `bundle install`:
|
||||
|
||||
```yaml
|
||||
image: ruby:2.3
|
||||
image: ruby:2.6
|
||||
|
||||
cache:
|
||||
paths:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
type: reference
|
||||
last_updated: 2018-06-04
|
||||
last_updated: 2020-01-06
|
||||
---
|
||||
|
||||
# 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:
|
||||
|
||||
```
|
||||
image: ruby:2.1
|
||||
image: ruby:2.6
|
||||
|
||||
pages:
|
||||
script:
|
||||
|
|
|
@ -178,6 +178,15 @@ module API
|
|||
expose :only_protected_branches
|
||||
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
|
||||
expose :import_status
|
||||
|
||||
|
@ -276,6 +285,8 @@ module API
|
|||
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
|
||||
expose :resolve_outdated_diff_discussions
|
||||
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
|
||||
# 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
|
||||
super(projects_relation).preload(:group)
|
||||
.preload(:ci_cd_settings)
|
||||
.preload(:container_expiration_policy)
|
||||
.preload(:auto_devops)
|
||||
.preload(project_group_links: { group: :route },
|
||||
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 :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_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 :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
|
||||
optional :public_builds, type: Boolean, desc: 'Perform public builds'
|
||||
|
@ -72,6 +75,14 @@ module API
|
|||
params :optional_update_params_ee do
|
||||
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
|
||||
[
|
||||
:auto_devops_enabled,
|
||||
|
@ -84,6 +95,7 @@ module API
|
|||
:ci_config_path,
|
||||
:ci_default_git_depth,
|
||||
:container_registry_enabled,
|
||||
:container_expiration_policy_attributes,
|
||||
:default_branch,
|
||||
:description,
|
||||
:autoclose_referenced_issues,
|
||||
|
|
|
@ -77,6 +77,7 @@ dependency_scanning:
|
|||
services: []
|
||||
except:
|
||||
variables:
|
||||
- $DEPENDENCY_SCANNING_DISABLED
|
||||
- $DS_DISABLE_DIND == 'false'
|
||||
script:
|
||||
- /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}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not delete project ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not fetch projects"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7122,6 +7125,9 @@ msgstr ""
|
|||
msgid "Error deleting %{issuableType}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error deleting project. Check logs for error details."
|
||||
msgstr ""
|
||||
|
||||
msgid "Error details"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
msgid "Self monitoring project does not exist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Self-monitoring is not enabled on this GitLab server, contact your administrator."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -124,14 +124,26 @@ describe GitlabSchema do
|
|||
|
||||
describe '.object_from_id' do
|
||||
context 'for subclasses of `ApplicationRecord`' do
|
||||
it 'returns the correct record' do
|
||||
user = create(:user)
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it 'returns the correct record' do
|
||||
result = described_class.object_from_id(user.to_global_id.to_s)
|
||||
|
||||
expect(result.sync).to eq(user)
|
||||
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
|
||||
user1 = create(:user)
|
||||
user2 = create(:user)
|
||||
|
|
|
@ -7,7 +7,16 @@ describe GitlabSchema.types['Query'] do
|
|||
expect(described_class.graphql_name).to eq('Query')
|
||||
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
|
||||
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
|
||||
expect(subject.name).to eq('jupyter')
|
||||
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.repository).to eq('https://jupyterhub.github.io/helm-chart/')
|
||||
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') }
|
||||
|
||||
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
|
||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
|||
describe 'CycleAnalytics#code' do
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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
|
||||
generate_cycle_analytics_spec(
|
||||
|
@ -24,8 +25,6 @@ describe 'CycleAnalytics#code' do
|
|||
context.create_merge_request_closing_issue(context.user, context.project, data[:issue])
|
||||
end]],
|
||||
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)
|
||||
|
||||
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])
|
||||
end]],
|
||||
post_fn: -> (context, data) do
|
||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
||||
end)
|
||||
|
||||
context "when a regular merge request (that doesn't close the issue) is created" do
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe CycleAnalytics::GroupLevel do
|
||||
let(:group) { create(:group)}
|
||||
let(:project) { create(:project, :repository, namespace: group) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:group) { create(:group)}
|
||||
let_it_be(:project) { create(:project, :repository, namespace: group) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
let_it_be(:user) { create(:user, :admin) }
|
||||
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(: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
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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(
|
||||
phase: :issue,
|
||||
|
@ -28,10 +29,6 @@ describe 'CycleAnalytics#issue' do
|
|||
end
|
||||
end]],
|
||||
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)
|
||||
|
||||
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
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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(
|
||||
phase: :plan,
|
||||
data_fn: -> (context) do
|
||||
{
|
||||
issue: context.create(:issue, project: context.project),
|
||||
issue: context.build(:issue, project: context.project),
|
||||
branch_name: context.generate(:branch)
|
||||
}
|
||||
end,
|
||||
|
@ -32,8 +33,6 @@ describe 'CycleAnalytics#plan' do
|
|||
context.create_commit_referencing_issue(data[:issue], branch_name: data[:branch_name])
|
||||
end]],
|
||||
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)
|
||||
|
||||
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
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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(
|
||||
phase: :production,
|
||||
|
@ -24,13 +25,7 @@ describe 'CycleAnalytics#production' do
|
|||
["production deploy happens after merge request is merged (along with other changes)",
|
||||
lambda do |context, data|
|
||||
# Make other changes on master
|
||||
sha = context.project.repository.create_file(
|
||||
context.user,
|
||||
context.generate(:branch),
|
||||
'content',
|
||||
message: 'commit message',
|
||||
branch_name: 'master')
|
||||
context.project.repository.commit(sha)
|
||||
context.project.repository.commit("sha_that_does_not_matter")
|
||||
|
||||
context.deploy_master(context.user, context.project)
|
||||
end]])
|
||||
|
@ -47,7 +42,7 @@ describe 'CycleAnalytics#production' do
|
|||
|
||||
context "when the deployment happens to a non-production environment" 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)
|
||||
MergeRequests::MergeService.new(project, user).execute(merge_request)
|
||||
deploy_master(user, project, environment: 'staging')
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe CycleAnalytics::ProjectLevel do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||
let(:milestone) { create(:milestone, project: project) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
let_it_be(:user) { create(:user, :admin) }
|
||||
let_it_be(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
|
||||
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(: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
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
let_it_be(:user) { create(:user, :admin) }
|
||||
|
||||
subject { CycleAnalytics::ProjectLevel.new(project, options: { from: from_date }) }
|
||||
|
||||
|
|
|
@ -5,11 +5,12 @@ require 'spec_helper'
|
|||
describe 'CycleAnalytics#staging' do
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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(
|
||||
phase: :staging,
|
||||
|
@ -28,14 +29,7 @@ describe 'CycleAnalytics#staging' do
|
|||
["production deploy happens after merge request is merged (along with other changes)",
|
||||
lambda do |context, data|
|
||||
# Make other changes on master
|
||||
sha = context.project.repository.create_file(
|
||||
context.user,
|
||||
context.generate(:branch),
|
||||
'content',
|
||||
message: 'commit message',
|
||||
branch_name: 'master')
|
||||
context.project.repository.commit(sha)
|
||||
|
||||
context.project.repository.commit("this_sha_apparently_does_not_matter")
|
||||
context.deploy_master(context.user, context.project)
|
||||
end]])
|
||||
|
||||
|
|
|
@ -5,16 +5,19 @@ require 'spec_helper'
|
|||
describe 'CycleAnalytics#test' do
|
||||
extend CycleAnalyticsHelpers::TestGeneration
|
||||
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:from_date) { 10.days.ago }
|
||||
let(:user) { create(:user, :admin) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:from_date) { 10.days.ago }
|
||||
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(
|
||||
phase: :test,
|
||||
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)
|
||||
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 }
|
||||
|
@ -22,20 +25,15 @@ describe 'CycleAnalytics#test' do
|
|||
start_time_conditions: [["pipeline is started", -> (context, data) { data[:pipeline].run! }]],
|
||||
end_time_conditions: [["pipeline is finished", -> (context, data) { data[:pipeline].succeed! }]],
|
||||
post_fn: -> (context, data) do
|
||||
context.merge_merge_requests_closing_issue(context.user, context.project, data[:issue])
|
||||
end)
|
||||
|
||||
context "when the pipeline is for a regular merge request (that doesn't close an issue)" 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.run!
|
||||
pipeline.succeed!
|
||||
|
||||
merge_merge_requests_closing_issue(user, project, issue)
|
||||
|
||||
expect(subject[:test].project_median).to be_nil
|
||||
end
|
||||
end
|
||||
|
@ -53,30 +51,22 @@ describe 'CycleAnalytics#test' do
|
|||
|
||||
context "when the pipeline is dropped (failed)" 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.run!
|
||||
pipeline.drop!
|
||||
|
||||
merge_merge_requests_closing_issue(user, project, issue)
|
||||
|
||||
expect(subject[:test].project_median).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the pipeline is cancelled" 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.run!
|
||||
pipeline.cancel!
|
||||
|
||||
merge_merge_requests_closing_issue(user, project, issue)
|
||||
|
||||
expect(subject[:test].project_median).to be_nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,4 +26,34 @@ describe ExternalWikiService do
|
|||
it { is_expected.not_to validate_presence_of(:external_wiki_url) }
|
||||
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
|
||||
|
|
|
@ -2251,6 +2251,22 @@ describe API::Projects do
|
|||
put api("/projects/#{project3.id}", user4), params: project_param
|
||||
expect(response).to have_gitlab_http_status(403)
|
||||
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
|
||||
|
||||
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.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 "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
|
||||
|
@ -56,8 +58,6 @@ module CycleAnalyticsHelpers
|
|||
end
|
||||
|
||||
context "when the data belongs to another project" do
|
||||
let(:other_project) { create(:project, :repository) }
|
||||
|
||||
it "returns nil" do
|
||||
# Use a stub to "trick" the data/condition functions
|
||||
# into using another project. This saves us from having to
|
||||
|
|
|
@ -105,12 +105,15 @@ module GraphqlHelpers
|
|||
end
|
||||
|
||||
def query_graphql_field(name, attributes = {}, fields = nil)
|
||||
fields ||= all_graphql_fields_for(name.classify)
|
||||
attributes = attributes_to_graphql(attributes)
|
||||
attributes = "(#{attributes})" if attributes.present?
|
||||
field_params = if attributes.present?
|
||||
"(#{attributes_to_graphql(attributes)})"
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
<<~QUERY
|
||||
#{name}#{attributes}
|
||||
#{wrap_fields(fields)}
|
||||
#{GraphqlHelpers.fieldnamerize(name.to_s)}#{field_params}
|
||||
#{wrap_fields(fields || all_graphql_fields_for(name.to_s.classify))}
|
||||
QUERY
|
||||
end
|
||||
|
||||
|
@ -301,6 +304,17 @@ module GraphqlHelpers
|
|||
def global_id_of(model)
|
||||
model.to_global_id.to_s
|
||||
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
|
||||
|
||||
# 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) }
|
||||
end
|
||||
|
||||
@allow_extra = false
|
||||
|
||||
chain :only do
|
||||
@allow_extra = false
|
||||
end
|
||||
|
||||
chain :at_least do
|
||||
@allow_extra = true
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
failure_message do |kls|
|
||||
|
@ -22,7 +36,7 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
|
|||
message = []
|
||||
|
||||
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")
|
||||
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