Add latest changes from gitlab-org/gitlab@master
|
@ -43,7 +43,7 @@ review-build-cng:
|
|||
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
||||
REVIEW_APPS_DOMAIN: "temp.gitlab-review.app" # FIXME: using temporary domain
|
||||
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
|
||||
GITLAB_HELM_CHART_REF: "master"
|
||||
GITLAB_HELM_CHART_REF: "v4.1.3"
|
||||
environment:
|
||||
name: review/${CI_COMMIT_REF_NAME}
|
||||
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
|
||||
|
|
2
Gemfile
|
@ -500,3 +500,5 @@ gem 'valid_email', '~> 0.1'
|
|||
# JSON
|
||||
gem 'json', '~> 2.3.0'
|
||||
gem 'json-schema', '~> 2.8.0'
|
||||
gem 'oj', '~> 3.10.6'
|
||||
gem 'multi_json', '~> 1.14.1'
|
||||
|
|
|
@ -687,6 +687,7 @@ GEM
|
|||
octokit (4.15.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
oj (3.10.6)
|
||||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
|
@ -1312,6 +1313,7 @@ DEPENDENCIES
|
|||
mimemagic (~> 0.3.2)
|
||||
mini_magick
|
||||
minitest (~> 5.11.0)
|
||||
multi_json (~> 1.14.1)
|
||||
nakayoshi_fork (~> 0.0.4)
|
||||
net-ldap
|
||||
net-ntp
|
||||
|
@ -1319,6 +1321,7 @@ DEPENDENCIES
|
|||
nokogiri (~> 1.10.9)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.15)
|
||||
oj (~> 3.10.6)
|
||||
omniauth (~> 1.8)
|
||||
omniauth-auth0 (~> 2.0.0)
|
||||
omniauth-authentiq (~> 0.3.3)
|
||||
|
|
BIN
app/assets/images/bot_avatars/alert-bot.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
app/assets/images/bot_avatars/security-bot.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
app/assets/images/bot_avatars/support-bot.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
|
@ -26,6 +26,7 @@ import Tracking from '~/tracking';
|
|||
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
|
||||
import SystemNote from './system_notes/system_note.vue';
|
||||
import AlertSidebar from './alert_sidebar.vue';
|
||||
import AlertMetrics from './alert_metrics.vue';
|
||||
|
||||
const containerEl = document.querySelector('.page-with-contextual-sidebar');
|
||||
|
||||
|
@ -36,6 +37,7 @@ export default {
|
|||
),
|
||||
fullAlertDetailsTitle: s__('AlertManagement|Alert details'),
|
||||
overviewTitle: s__('AlertManagement|Overview'),
|
||||
metricsTitle: s__('AlertManagement|Metrics'),
|
||||
reportedAt: s__('AlertManagement|Reported %{when}'),
|
||||
reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
|
||||
},
|
||||
|
@ -53,6 +55,7 @@ export default {
|
|||
TimeAgoTooltip,
|
||||
AlertSidebar,
|
||||
SystemNote,
|
||||
AlertMetrics,
|
||||
},
|
||||
inject: {
|
||||
projectPath: {
|
||||
|
@ -329,6 +332,9 @@ export default {
|
|||
</template>
|
||||
</gl-table>
|
||||
</gl-tab>
|
||||
<gl-tab data-testId="metricsTab" :title="$options.i18n.metricsTitle">
|
||||
<alert-metrics :dashboard-url="alert.metricsDashboardUrl" />
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
<alert-sidebar
|
||||
:alert="alert"
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default {
|
||||
props: {
|
||||
dashboardUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
metricEmbedComponent: null,
|
||||
namespace: 'alertMetrics',
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.dashboardUrl) {
|
||||
Promise.all([
|
||||
import('~/monitoring/components/embeds/metric_embed.vue'),
|
||||
import('~/monitoring/stores'),
|
||||
])
|
||||
.then(([{ default: MetricEmbed }, { monitoringDashboard }]) => {
|
||||
this.$store = new Vuex.Store({
|
||||
modules: {
|
||||
[this.namespace]: monitoringDashboard,
|
||||
},
|
||||
});
|
||||
this.metricEmbedComponent = MetricEmbed;
|
||||
})
|
||||
.catch(e => Sentry.captureException(e));
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-py-3">
|
||||
<div v-if="dashboardUrl" ref="metricsChart">
|
||||
<component
|
||||
:is="metricEmbedComponent"
|
||||
v-if="metricEmbedComponent"
|
||||
:dashboard-url="dashboardUrl"
|
||||
:namespace="namespace"
|
||||
/>
|
||||
</div>
|
||||
<div v-else ref="emptyState">
|
||||
{{ s__("AlertManagement|Metrics weren't available in the alerts payload.") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -5,6 +5,7 @@ fragment AlertDetailItem on AlertManagementAlert {
|
|||
...AlertListItem
|
||||
createdAt
|
||||
monitoringTool
|
||||
metricsDashboardUrl
|
||||
service
|
||||
description
|
||||
updatedAt
|
||||
|
@ -15,4 +16,5 @@ fragment AlertDetailItem on AlertManagementAlert {
|
|||
...AlertNote
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import { GlDeprecatedButton } from '@gitlab/ui';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { getMilestone } from 'ee_else_ce/boards/boards_util';
|
||||
import ListIssue from 'ee_else_ce/boards/models/issue';
|
||||
import eventHub from '../eventhub';
|
||||
|
@ -11,7 +11,7 @@ export default {
|
|||
name: 'BoardNewIssue',
|
||||
components: {
|
||||
ProjectSelect,
|
||||
GlDeprecatedButton,
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
groupId: {
|
||||
|
@ -120,21 +120,18 @@ export default {
|
|||
/>
|
||||
<project-select v-if="groupId" :group-id="groupId" :list="list" />
|
||||
<div class="clearfix prepend-top-10">
|
||||
<gl-deprecated-button
|
||||
<gl-button
|
||||
ref="submit-button"
|
||||
:disabled="disabled"
|
||||
class="float-left"
|
||||
variant="success"
|
||||
category="primary"
|
||||
type="submit"
|
||||
>{{ __('Submit issue') }}</gl-deprecated-button
|
||||
>
|
||||
<gl-deprecated-button
|
||||
class="float-right"
|
||||
type="button"
|
||||
variant="default"
|
||||
@click="cancel"
|
||||
>{{ __('Cancel') }}</gl-deprecated-button
|
||||
>{{ __('Submit issue') }}</gl-button
|
||||
>
|
||||
<gl-button class="float-right" type="button" variant="default" @click="cancel">{{
|
||||
__('Cancel')
|
||||
}}</gl-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -374,8 +374,8 @@ export const fetchDashboardValidationWarnings = ({ state, dispatch }) => {
|
|||
},
|
||||
})
|
||||
.then(resp => resp.data?.project?.environments?.nodes?.[0]?.metricsDashboard)
|
||||
.then(({ schemaValidationWarnings }) => {
|
||||
const hasWarnings = schemaValidationWarnings && schemaValidationWarnings.length !== 0;
|
||||
.then(({ schemaValidationWarnings } = {}) => {
|
||||
const hasWarnings = schemaValidationWarnings?.length !== 0;
|
||||
/**
|
||||
* The payload of the dispatch is a boolean, because at the moment a standard
|
||||
* warning message is shown instead of the warnings the BE returns
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
|
||||
import initPipelines from '~/commit/pipelines/pipelines_bundle';
|
||||
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new MiniPipelineGraph({
|
||||
|
@ -8,5 +9,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
}).bindEvents();
|
||||
// eslint-disable-next-line no-jquery/no-load
|
||||
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
|
||||
fetchCommitMergeRequests();
|
||||
initPipelines();
|
||||
});
|
||||
|
|
23
app/helpers/ci/jobs_helper.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ci
|
||||
module JobsHelper
|
||||
def jobs_data
|
||||
{
|
||||
"endpoint" => project_job_path(@project, @build, format: :json),
|
||||
"project_path" => @project.full_path,
|
||||
"deployment_help_url" => help_page_path('user/project/clusters/index.html', anchor: 'troubleshooting'),
|
||||
"runner_help_url" => help_page_path('ci/runners/README.html', anchor: 'set-maximum-job-timeout-for-a-runner'),
|
||||
"runner_settings_url" => project_runners_path(@build.project, anchor: 'js-runners-settings'),
|
||||
"variables_settings_url" => project_variables_path(@build.project, anchor: 'js-cicd-variables-settings'),
|
||||
"page_path" => project_job_path(@project, @build),
|
||||
"build_status" => @build.status,
|
||||
"build_stage" => @build.stage,
|
||||
"log_state" => '',
|
||||
"build_options" => javascript_build_options
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Ci::JobsHelper.prepend_if_ee('::EE::Ci::JobsHelper')
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module JobsHelper
|
||||
def jobs_data
|
||||
{
|
||||
"endpoint" => project_job_path(@project, @build, format: :json),
|
||||
"project_path" => @project.full_path,
|
||||
"deployment_help_url" => help_page_path('user/project/clusters/index.md', anchor: 'troubleshooting'),
|
||||
"runner_help_url" => help_page_path('ci/runners/README.md', anchor: 'set-maximum-job-timeout-for-a-runner'),
|
||||
"runner_settings_url" => project_runners_path(@build.project, anchor: 'js-runners-settings'),
|
||||
"variables_settings_url" => project_variables_path(@build.project, anchor: 'js-cicd-variables-settings'),
|
||||
"page_path" => project_job_path(@project, @build),
|
||||
"build_status" => @build.status,
|
||||
"build_stage" => @build.stage,
|
||||
"log_state" => '',
|
||||
"build_options" => javascript_build_options
|
||||
}
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
class AuditEvent < ApplicationRecord
|
||||
include CreatedAtFilterable
|
||||
include IgnorableColumns
|
||||
include BulkInsertSafe
|
||||
|
||||
ignore_column :updated_at, remove_with: '13.4', remove_after: '2020-09-22'
|
||||
|
||||
|
|
|
@ -36,6 +36,12 @@ module Avatarable
|
|||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def bot_avatar(image:)
|
||||
Rails.root.join('app', 'assets', 'images', 'bot_avatars', image).open
|
||||
end
|
||||
end
|
||||
|
||||
def avatar_type
|
||||
unless self.avatar.image?
|
||||
errors.add :avatar, "file format is not supported. Please try one of the following supported formats: #{AvatarUploader::SAFE_IMAGE_EXT.join(', ')}"
|
||||
|
|
|
@ -642,6 +642,7 @@ class User < ApplicationRecord
|
|||
unique_internal(where(user_type: :alert_bot), 'alert-bot', email_pattern) do |u|
|
||||
u.bio = 'The GitLab alert bot'
|
||||
u.name = 'GitLab Alert Bot'
|
||||
u.avatar = bot_avatar(image: 'alert-bot.png')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -661,6 +662,7 @@ class User < ApplicationRecord
|
|||
unique_internal(where(user_type: :support_bot), 'support-bot', email_pattern) do |u|
|
||||
u.bio = 'The GitLab support bot used for Service Desk'
|
||||
u.name = 'GitLab Support Bot'
|
||||
u.avatar = bot_avatar(image: 'support-bot.png')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
- if user.note.present?
|
||||
%span.has-tooltip.user-note{ title: user.note }
|
||||
= icon("sticky-note-o cgrey")
|
||||
= sprite_icon('document', size: 16, css_class: 'gl-vertical-align-middle')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add custom avatars for Alert and Support Bot
|
||||
merge_request: 36269
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/217768-surface-link-to-chart.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Surface metrics charts on the alert detail page
|
||||
merge_request: 35044
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix infinite loading spinner for related merge requests on commit pipelines
|
||||
tab
|
||||
merge_request: 36077
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/swap-to-oj.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add oj gem for faster JSON
|
||||
merge_request: 35527
|
||||
author:
|
||||
type: performance
|
5
config/initializers/multi_json.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Explicitly set the JSON adapter used by MultiJson
|
||||
# Currently we want this to default to the existing json gem
|
||||
MultiJson.use(:json_gem)
|
4
config/initializers/oj.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Ensure Oj runs in json-gem compatibility mode by default
|
||||
Oj.default_options = { mode: :rails }
|
|
@ -243,6 +243,38 @@ Prometheus alert payloads sent to the `notify.json` endpoint are limited to 1 MB
|
|||
|
||||
Alert payloads sent to the `notify.json` endpoint are limited to 1 MB in size.
|
||||
|
||||
### Metrics dashboard YAML files
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34834) in GitLab 13.2.
|
||||
|
||||
The memory occupied by a parsed metrics dashboard YAML file cannot exceed 1 MB.
|
||||
|
||||
The maximum depth of each YAML file is limited to 100. The maximum depth of a YAML
|
||||
file is the amount of nesting of its most nested key. Each hash and array on the
|
||||
path of the most nested key counts towards its depth. For example, the depth of the
|
||||
most nested key in the following YAML is 7:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Test dashboard'
|
||||
links:
|
||||
- title: Link 1
|
||||
url: https://gitlab.com
|
||||
panel_groups:
|
||||
- group: Group A
|
||||
priority: 1
|
||||
panels:
|
||||
- title: "Super Chart A1"
|
||||
type: "area-chart"
|
||||
y_label: "y_label"
|
||||
weight: 1
|
||||
max_value: 1
|
||||
metrics:
|
||||
- id: metric_a1
|
||||
query_range: 'query'
|
||||
unit: unit
|
||||
label: Legend Label
|
||||
```
|
||||
|
||||
## Environment data on Deploy Boards
|
||||
|
||||
[Deploy Boards](../user/project/deploy_boards.md) load information from Kubernetes about
|
||||
|
|
|
@ -12,16 +12,16 @@ For a full list of reference architectures, see
|
|||
> - **High Availability:** False
|
||||
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git: 4 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|--------------------------------------------------------------|-----------|---------------------------------|---------------|-----------------------|----------------|
|
||||
| Load balancer | 1 | 2 vCPU, 1.8GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Object storage | n/a | n/a | n/a | n/a | n/a |
|
||||
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
|
||||
| PostgreSQL | 1 | 2 vCPU, 7.5GB memory | n1-standard-2 | m5.large | D2s v3 |
|
||||
| Redis | 1 | 1 vCPU, 3.75GB memory | n1-standard-1 | m5.large | D2s v3 |
|
||||
| Gitaly | 1 | 4 vCPU, 15GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
|
||||
| GitLab Rails | 2 | 8 vCPU, 7.2GB memory | n1-highcpu-8 | c5.2xlarge | F8s v2 |
|
||||
| Monitoring node | 1 | 2 vCPU, 1.8GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|------------------------------------------|--------|-------------------------|-----------------|----------------|-----------|
|
||||
| Load balancer | 1 | 2 vCPU, 1.8GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
|
||||
| PostgreSQL | 1 | 2 vCPU, 7.5GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
|
||||
| Redis | 1 | 1 vCPU, 3.75GB memory | `n1-standard-1` | `m5.large` | `D2s v3` |
|
||||
| Gitaly | 1 | 4 vCPU, 15GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
|
||||
| GitLab Rails | 2 | 8 vCPU, 7.2GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
|
||||
| Monitoring node | 1 | 2 vCPU, 1.8GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
|
||||
| Object storage | n/a | n/a | n/a | n/a | n/a |
|
||||
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
|
||||
|
||||
The Google Cloud Platform (GCP) architectures were built and tested using the
|
||||
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
|
||||
|
@ -41,12 +41,6 @@ To set up GitLab and its components to accommodate up to 2,000 users:
|
|||
|
||||
1. [Configure the external load balancing node](#configure-the-load-balancer)
|
||||
to handle the load balancing of the two GitLab application services nodes.
|
||||
1. [Configure the object storage](#configure-the-object-storage) used for
|
||||
shared data objects.
|
||||
1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
|
||||
to have shared disk storage service as an alternative to Gitaly or object
|
||||
storage. You can skip this step if you're not using GitLab Pages (which
|
||||
requires NFS).
|
||||
1. [Configure PostgreSQL](#configure-postgresql), the database for GitLab.
|
||||
1. [Configure Redis](#configure-redis).
|
||||
1. [Configure Gitaly](#configure-gitaly), which provides access to the Git
|
||||
|
@ -56,6 +50,12 @@ To set up GitLab and its components to accommodate up to 2,000 users:
|
|||
requests (which include UI, API, and Git over HTTP/SSH).
|
||||
1. [Configure Prometheus](#configure-prometheus) to monitor your GitLab
|
||||
environment.
|
||||
1. [Configure the object storage](#configure-the-object-storage) used for
|
||||
shared data objects.
|
||||
1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
|
||||
to have shared disk storage service as an alternative to Gitaly or object
|
||||
storage. You can skip this step if you're not using GitLab Pages (which
|
||||
requires NFS).
|
||||
|
||||
## Configure the load balancer
|
||||
|
||||
|
@ -170,73 +170,6 @@ Configure DNS for an alternate SSH hostname, such as `altssh.gitlab.example.com`
|
|||
</a>
|
||||
</div>
|
||||
|
||||
## Configure the object storage
|
||||
|
||||
GitLab supports using an object storage service for holding several types of
|
||||
data, and is recommended over [NFS](#configure-nfs-optional). In general,
|
||||
object storage services are better for larger environments, as object storage
|
||||
is typically much more performant, reliable, and scalable.
|
||||
|
||||
Object storage options that GitLab has either tested or is aware of customers
|
||||
using, includes:
|
||||
|
||||
- SaaS/Cloud solutions (such as [Amazon S3](https://aws.amazon.com/s3/) or
|
||||
[Google Cloud Storage](https://cloud.google.com/storage)).
|
||||
- On-premises hardware and appliances, from various storage vendors.
|
||||
- MinIO ([Deployment guide](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html)).
|
||||
|
||||
To configure GitLab to use object storage, refer to the following guides based
|
||||
on the features you intend to use:
|
||||
|
||||
1. [Object storage for backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage).
|
||||
1. [Object storage for job artifacts](../job_artifacts.md#using-object-storage)
|
||||
including [incremental logging](../job_logs.md#new-incremental-logging-architecture).
|
||||
1. [Object storage for LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage).
|
||||
1. [Object storage for uploads](../uploads.md#using-object-storage-core-only).
|
||||
1. [Object storage for merge request diffs](../merge_request_diffs.md#using-object-storage).
|
||||
1. [Object storage for Container Registry](../packages/container_registry.md#container-registry-storage-driver) (optional feature).
|
||||
1. [Object storage for Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage) (optional feature).
|
||||
1. [Object storage for packages](../packages/index.md#using-object-storage) (optional feature). **(PREMIUM ONLY)**
|
||||
1. [Object storage for Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature). **(PREMIUM ONLY)**
|
||||
1. [Object storage for Pseudonymizer](../pseudonymizer.md#configuration) (optional feature). **(ULTIMATE ONLY)**
|
||||
1. [Object storage for autoscale Runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional, for improved performance).
|
||||
1. [Object storage for Terraform state files](../terraform_state.md#using-object-storage-core-only).
|
||||
|
||||
Using separate buckets for each data type is the recommended approach for GitLab.
|
||||
|
||||
A limitation of our configuration is that each use of object storage is
|
||||
separately configured. We have an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/23345)
|
||||
for improving this, which would allow for one bucket with separate folders.
|
||||
|
||||
Using a single bucket when GitLab is deployed with the Helm chart causes
|
||||
restoring from a backup to
|
||||
[not function properly](https://docs.gitlab.com/charts/advanced/external-object-storage/#lfs-artifacts-uploads-packages-external-diffs-pseudonymizer).
|
||||
Although you may not be using a Helm deployment right now, if you migrate
|
||||
GitLab to a Helm deployment later, GitLab would still work, but you may not
|
||||
realize backups aren't working correctly until a critical requirement for
|
||||
functioning backups is encountered.
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Configure NFS (optional)
|
||||
|
||||
For improved performance, [object storage](#configure-the-object-storage),
|
||||
along with [Gitaly](#configure-gitaly), are recommended over using NFS whenever
|
||||
possible. However, if you intend to use GitLab Pages,
|
||||
[you must use NFS](troubleshooting.md#gitlab-pages-requires-nfs).
|
||||
|
||||
For information about configuring NFS, see the [NFS documentation page](../high_availability/nfs.md).
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Configure PostgreSQL
|
||||
|
||||
In this section, you'll be guided through configuring an external PostgreSQL database
|
||||
|
@ -859,6 +792,73 @@ running [Prometheus](../monitoring/prometheus/index.md) and
|
|||
</a>
|
||||
</div>
|
||||
|
||||
## Configure the object storage
|
||||
|
||||
GitLab supports using an object storage service for holding several types of
|
||||
data, and is recommended over [NFS](#configure-nfs-optional). In general,
|
||||
object storage services are better for larger environments, as object storage
|
||||
is typically much more performant, reliable, and scalable.
|
||||
|
||||
Object storage options that GitLab has either tested or is aware of customers
|
||||
using, includes:
|
||||
|
||||
- SaaS/Cloud solutions (such as [Amazon S3](https://aws.amazon.com/s3/) or
|
||||
[Google Cloud Storage](https://cloud.google.com/storage)).
|
||||
- On-premises hardware and appliances, from various storage vendors.
|
||||
- MinIO ([Deployment guide](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html)).
|
||||
|
||||
To configure GitLab to use object storage, refer to the following guides based
|
||||
on the features you intend to use:
|
||||
|
||||
1. [Object storage for backups](../../raketasks/backup_restore.md#uploading-backups-to-a-remote-cloud-storage).
|
||||
1. [Object storage for job artifacts](../job_artifacts.md#using-object-storage)
|
||||
including [incremental logging](../job_logs.md#new-incremental-logging-architecture).
|
||||
1. [Object storage for LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage).
|
||||
1. [Object storage for uploads](../uploads.md#using-object-storage-core-only).
|
||||
1. [Object storage for merge request diffs](../merge_request_diffs.md#using-object-storage).
|
||||
1. [Object storage for Container Registry](../packages/container_registry.md#container-registry-storage-driver) (optional feature).
|
||||
1. [Object storage for Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage) (optional feature).
|
||||
1. [Object storage for packages](../packages/index.md#using-object-storage) (optional feature). **(PREMIUM ONLY)**
|
||||
1. [Object storage for Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature). **(PREMIUM ONLY)**
|
||||
1. [Object storage for Pseudonymizer](../pseudonymizer.md#configuration) (optional feature). **(ULTIMATE ONLY)**
|
||||
1. [Object storage for autoscale Runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional, for improved performance).
|
||||
1. [Object storage for Terraform state files](../terraform_state.md#using-object-storage-core-only).
|
||||
|
||||
Using separate buckets for each data type is the recommended approach for GitLab.
|
||||
|
||||
A limitation of our configuration is that each use of object storage is
|
||||
separately configured. We have an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/23345)
|
||||
for improving this, which would allow for one bucket with separate folders.
|
||||
|
||||
Using a single bucket when GitLab is deployed with the Helm chart causes
|
||||
restoring from a backup to
|
||||
[not function properly](https://docs.gitlab.com/charts/advanced/external-object-storage/#lfs-artifacts-uploads-packages-external-diffs-pseudonymizer).
|
||||
Although you may not be using a Helm deployment right now, if you migrate
|
||||
GitLab to a Helm deployment later, GitLab would still work, but you may not
|
||||
realize backups aren't working correctly until a critical requirement for
|
||||
functioning backups is encountered.
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Configure NFS (optional)
|
||||
|
||||
For improved performance, [object storage](#configure-the-object-storage),
|
||||
along with [Gitaly](#configure-gitaly), are recommended over using NFS whenever
|
||||
possible. However, if you intend to use GitLab Pages,
|
||||
[you must use NFS](troubleshooting.md#gitlab-pages-requires-nfs).
|
||||
|
||||
For information about configuring NFS, see the [NFS documentation page](../high_availability/nfs.md).
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See the [troubleshooting documentation](troubleshooting.md).
|
||||
|
|
|
@ -19,7 +19,7 @@ Parameters:
|
|||
| `statistics` | boolean | no | Include group statistics (admins only) |
|
||||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
|
||||
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md#valid-access-levels) |
|
||||
| `top_level_only` | boolean | no | Limit to top level groups, excluding all subgroups |
|
||||
|
||||
```plaintext
|
||||
|
@ -131,7 +131,7 @@ Parameters:
|
|||
| `statistics` | boolean | no | Include group statistics (admins only) |
|
||||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
|
||||
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md#valid-access-levels) |
|
||||
|
||||
```plaintext
|
||||
GET /groups/:id/subgroups
|
||||
|
@ -194,7 +194,7 @@ Parameters:
|
|||
| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
|
||||
| `with_shared` | boolean | no | Include projects shared to this group. Default is `true` |
|
||||
| `include_subgroups` | boolean | no | Include projects in subgroups of this group. Default is `false` |
|
||||
| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md#valid-access-levels) |
|
||||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
| `with_security_reports` | boolean | no | **(ULTIMATE)** Return only projects that have security reports artifacts present in any of their builds. This means "projects with security reports enabled". Default is `false` |
|
||||
|
||||
|
@ -269,7 +269,7 @@ Parameters:
|
|||
| `starred` | boolean | no | Limit by projects starred by the current user |
|
||||
| `with_issues_enabled` | boolean | no | Limit by projects with issues feature enabled. Default is `false` |
|
||||
| `with_merge_requests_enabled` | boolean | no | Limit by projects with merge requests feature enabled. Default is `false` |
|
||||
| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit to projects where current user has at least this [access level](members.md#valid-access-levels) |
|
||||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
|
||||
Example response:
|
||||
|
@ -1052,7 +1052,7 @@ POST /groups/:id/ldap_group_links
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
|
||||
| `cn` | string | no | The CN of an LDAP group |
|
||||
| `filter` | string | no | The LDAP filter for the group |
|
||||
| `group_access` | integer | yes | Minimum access level for members of the LDAP group |
|
||||
| `group_access` | integer | yes | Minimum [access level](members.md#valid-access-levels) for members of the LDAP group |
|
||||
| `provider` | string | yes | LDAP provider for the LDAP group link |
|
||||
|
||||
NOTE: **Note:**
|
||||
|
@ -1141,7 +1141,7 @@ POST /groups/:id/share
|
|||
| --------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
|
||||
| `group_id` | integer | yes | The ID of the group to share with |
|
||||
| `group_access` | integer | yes | The [permissions level](members.md) to grant the group |
|
||||
| `group_access` | integer | yes | The [access level](members.md#valid-access-levels) to grant the group |
|
||||
| `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 |
|
||||
|
||||
### Delete link sharing group with another group
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Group and project members API
|
||||
|
||||
**Valid access levels**
|
||||
## Valid access levels
|
||||
|
||||
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ GET /projects
|
|||
| `with_programming_language` | string | no | Limit by projects which use the given programming language |
|
||||
| `wiki_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
|
||||
| `repository_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md#valid-access-levels) |
|
||||
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
|
||||
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
|
||||
| `last_activity_after` | datetime | no | Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ |
|
||||
|
@ -351,7 +351,7 @@ GET /users/:user_id/projects
|
|||
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
|
||||
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
|
||||
| `with_programming_language` | string | no | Limit by projects which use the given programming language |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md#valid-access-levels) |
|
||||
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
|
||||
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
|
||||
|
||||
|
@ -571,7 +571,7 @@ GET /users/:user_id/starred_projects
|
|||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only). |
|
||||
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature. |
|
||||
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature. |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md). |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md#valid-access-levels). |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/users/5/starred_projects"
|
||||
|
@ -1279,7 +1279,7 @@ GET /projects/:id/forks
|
|||
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
|
||||
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
|
||||
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
|
||||
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md#valid-access-levels) |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/forks"
|
||||
|
@ -1909,7 +1909,7 @@ POST /projects/:id/share
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
|
||||
| `group_id` | integer | yes | The ID of the group to share with |
|
||||
| `group_access` | integer | yes | The [permissions level](members.md) to grant the group |
|
||||
| `group_access` | integer | yes | The [access level](members.md#valid-access-levels) to grant the group |
|
||||
| `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 |
|
||||
|
||||
## Delete a shared project link within a group
|
||||
|
|
|
@ -545,9 +545,8 @@ runtime.
|
|||
support for using private registries, which required manual configuration
|
||||
of credentials on runner's host. We recommend to upgrade your Runner to
|
||||
at least version **1.8** if you want to use private registries.
|
||||
- Not available for [Kubernetes executor](https://docs.gitlab.com/runner/executors/kubernetes.html),
|
||||
follow <https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2673> for
|
||||
details.
|
||||
- Available for [Kubernetes executor](https://docs.gitlab.com/runner/executors/kubernetes.html)
|
||||
in GitLab Runner 13.1 and later.
|
||||
|
||||
### Using statically-defined credentials
|
||||
|
||||
|
@ -601,6 +600,7 @@ There are two ways to determine the value of `DOCKER_AUTH_CONFIG`:
|
|||
Open a terminal and execute the following command:
|
||||
|
||||
```shell
|
||||
# Note the use of "-n" - it prevents encoding a newline in the password.
|
||||
echo -n "my_username:my_password" | base64
|
||||
|
||||
# Example output to copy
|
||||
|
|
|
@ -196,7 +196,7 @@ GitLab can be considered to have two layers from a process perspective:
|
|||
- Process: `alertmanager`
|
||||
- GitLab.com: [Monitoring of GitLab.com](https://about.gitlab.com/handbook/engineering/monitoring/)
|
||||
|
||||
[Alert manager](https://prometheus.io/docs/alerting/latest/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue #45740](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45740) about what we will be alerting on.
|
||||
[Alert manager](https://prometheus.io/docs/alerting/latest/alertmanager/) is a tool provided by Prometheus that _"handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or Opsgenie. It also takes care of silencing and inhibition of alerts."_ You can read more in [issue #45740](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45740) about what we will be alerting on.
|
||||
|
||||
#### Certificate management
|
||||
|
||||
|
|
|
@ -646,7 +646,7 @@ defaultClient.query({ query })
|
|||
.then(result => console.log(result));
|
||||
```
|
||||
|
||||
When [using Vuex](#Using-with-Vuex), disable the cache when:
|
||||
When [using Vuex](#using-with-vuex), disable the cache when:
|
||||
|
||||
- The data is being cached elsewhere
|
||||
- The use case does not need caching
|
||||
|
|
|
@ -638,7 +638,7 @@ That concludes the configuration changes for our GitLab instance. Next, we'll cr
|
|||
|
||||
On the EC2 dashboard:
|
||||
|
||||
1. Select the `GitLab` instance we [created earlier](#install-gitLab).
|
||||
1. Select the `GitLab` instance we [created earlier](#install-gitlab).
|
||||
1. Click on **Actions**, scroll down to **Image** and click **Create Image**.
|
||||
1. Give your image a name and description (we'll use `GitLab-Source` for both).
|
||||
1. Leave everything else as default and click **Create Image**
|
||||
|
|
Before Width: | Height: | Size: 98 KiB |
BIN
doc/user/group/bulk_editing/img/bulk-editing_v13_2.png
Normal file
After Width: | Height: | Size: 121 KiB |
|
@ -13,7 +13,7 @@ For more details, see [Bulk editing issues and merge requests at the project lev
|
|||
If you want to update attributes across multiple issues, epics, or merge requests in a group, you
|
||||
can do it by bulk editing them, that is, editing them together.
|
||||
|
||||
![Bulk editing](img/bulk-editing.png)
|
||||
![Bulk editing](img/bulk-editing_v13_2.png)
|
||||
|
||||
## Bulk edit issues at the group level
|
||||
|
||||
|
@ -24,8 +24,12 @@ You need a permission level of [Reporter or higher](../../permissions.md) to man
|
|||
|
||||
When bulk editing issues in a group, you can edit the following attributes:
|
||||
|
||||
- Epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in
|
||||
[GitLab Premium](https://about.gitlab.com/pricing/) 13.2.) **(PREMIUM)**
|
||||
- Milestone
|
||||
- Labels
|
||||
- Health status ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in
|
||||
[GitLab Ultimate](https://about.gitlab.com/pricing/) 13.2.) **(ULTIMATE)**
|
||||
|
||||
To update multiple project issues at the same time:
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ requests you're assigned to.
|
|||
|
||||
[Snippets](snippets.md) are code blocks that you want to store in GitLab, from which
|
||||
you have quick access to. You can also gather feedback on them through
|
||||
[Discussions](#Discussions).
|
||||
[Discussions](#discussions).
|
||||
|
||||
## Keyboard shortcuts
|
||||
|
||||
|
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 33 KiB |
|
@ -257,7 +257,7 @@ can configure this manually as follows:
|
|||
|
||||
1. Running the pipeline displays the widget in the merge request, like this:
|
||||
|
||||
![MR Terraform widget](img/terraform_plan_widget_v13_2.png)
|
||||
![Merge Request Terraform widget](img/terraform_plan_widget_v13_2.png)
|
||||
|
||||
1. Clicking the **View Full Log** button in the widget takes you directly to the
|
||||
plan output present in the pipeline logs:
|
||||
|
|
|
@ -92,7 +92,7 @@ to change.
|
|||
GitLab makes full use of the standard (CommonMark) formatting, but also includes additional
|
||||
functionality useful for GitLab users.
|
||||
|
||||
It makes use of [new Markdown features](#new-GFM-markdown-extensions),
|
||||
It makes use of [new Markdown features](#new-gfm-markdown-extensions),
|
||||
not found in standard Markdown:
|
||||
|
||||
- [Color "chips" written in HEX, RGB or HSL](#colors)
|
||||
|
|
|
@ -25,7 +25,7 @@ To add or import a user, you can follow the
|
|||
|
||||
## Principles behind permissions
|
||||
|
||||
See our [product handbook on permissions](https://about.gitlab.com/handbook/product/#permissions-in-gitlab)
|
||||
See our [product handbook on permissions](https://about.gitlab.com/handbook/product/gitlab-the-product/#permissions-in-gitlab).
|
||||
|
||||
## Instance-wide user permissions
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ For more details, see
|
|||
If you want to update attributes across multiple issues or merge requests, you can do it
|
||||
by bulk editing them, that is, editing them together.
|
||||
|
||||
![Bulk editing](img/bulk-editing.png)
|
||||
![Bulk editing](img/bulk-editing_v13_2.png)
|
||||
|
||||
## Bulk edit issues at the project level
|
||||
|
||||
|
@ -25,8 +25,12 @@ When bulk editing issues in a project, you can edit the following attributes:
|
|||
|
||||
- Status (open/closed)
|
||||
- Assignee
|
||||
- Epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210470) in
|
||||
[GitLab Premium](https://about.gitlab.com/pricing/) 13.2.) **(PREMIUM)**
|
||||
- Milestone
|
||||
- Labels
|
||||
- Health status ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218395) in
|
||||
[GitLab Ultimate](https://about.gitlab.com/pricing/) 13.2.) **(ULTIMATE)**
|
||||
- Subscriptions
|
||||
|
||||
To update multiple project issues at the same time:
|
||||
|
|
Before Width: | Height: | Size: 193 KiB |
BIN
doc/user/project/img/bulk-editing_v13_2.png
Normal file
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 1.9 KiB |
|
@ -90,6 +90,19 @@ To remove dashboard from the favorites list, click the solid **Unstar Dashboard*
|
|||
|
||||
![Monitoring Dashboard favorite state toggle](img/toggle_metrics_user_starred_dashboard_v13_0.png)
|
||||
|
||||
##### Manage the metrics dashboard settings
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223204) in GitLab 13.2.
|
||||
|
||||
To manage the settings for your metrics dashboard:
|
||||
|
||||
1. Sign in as a user with project Maintainer or Admin
|
||||
[permissions](../../permissions.md#project-members-permissions).
|
||||
1. Navigate to your dashboard at **{cloud-gear}** **Operations > Metrics**.
|
||||
1. In the top-right corner of your dashboard, click **{settings}** **Metrics Settings**:
|
||||
|
||||
![Monitoring Dashboard actions menu with create new item](img/metrics_settings_button_v13_2.png)
|
||||
|
||||
#### About managed Prometheus deployments
|
||||
|
||||
Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/helm/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
|
||||
|
@ -274,15 +287,30 @@ The metrics as defined below do not support alerts, unlike
|
|||
|
||||
#### Adding a new dashboard to your project
|
||||
|
||||
> UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223204) in GitLab 13.2.
|
||||
|
||||
You can configure a custom dashboard by adding a new YAML file into your project's
|
||||
`.gitlab/dashboards/` directory. In order for the dashboards to be displayed on
|
||||
the project's **Operations > Metrics** page, the files must have a `.yml`
|
||||
the project's **{cloud-gear}** **Operations > Metrics** page, the files must have a `.yml`
|
||||
extension and should be present in the project's **default** branch.
|
||||
|
||||
For example:
|
||||
To create a new dashboard from the GitLab user interface:
|
||||
|
||||
1. Sign in to GitLab as a user with Maintainer or Owner
|
||||
[permissions](../../permissions.md#project-members-permissions).
|
||||
1. Navigate to your dashboard at **{cloud-gear}** **Operations > Metrics**.
|
||||
1. In the top-right corner of your dashboard, click the **{file-addition-solid}** **Actions** menu,
|
||||
and select **Create new**:
|
||||
![Monitoring Dashboard actions menu with create new item](img/actions_menu_create_new_dashboard_v13_2.png)
|
||||
1. In the modal window, click **Open Repository**, then follow the instructions
|
||||
for creating a new dashboard from the command line.
|
||||
|
||||
To create a new dashboard from the command line:
|
||||
|
||||
1. Create `.gitlab/dashboards/prom_alerts.yml` under your repository's root
|
||||
directory with the following contents:
|
||||
directory. Each YAML file should define the layout of the dashboard and the
|
||||
Prometheus queries used to populate data. This example dashboard displays a
|
||||
single area chart:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
|
@ -302,10 +330,6 @@ For example:
|
|||
unit: "count"
|
||||
```
|
||||
|
||||
The above sample dashboard would display a single area chart. Each file should
|
||||
define the layout of the dashboard and the Prometheus queries used to populate
|
||||
data.
|
||||
|
||||
1. Save the file, commit, and push to your repository. The file must be present in your **default** branch.
|
||||
1. Navigate to your project's **Operations > Metrics** and choose the custom
|
||||
dashboard from the dropdown.
|
||||
|
@ -324,7 +348,7 @@ Resulting `.yml` file can be customized and adapted to your project.
|
|||
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
|
||||
new branch.
|
||||
|
||||
1. Click **Duplicate dashboard** in the dashboard dropdown.
|
||||
1. Click **Duplicate dashboard** in the dashboard dropdown or in the actions menu.
|
||||
|
||||
NOTE: **Note:**
|
||||
You can duplicate only GitLab-defined dashboards.
|
||||
|
|
|
@ -88,7 +88,7 @@ An issue can be assigned to:
|
|||
|
||||
- Yourself.
|
||||
- Another person.
|
||||
- [Many people](#multiple-assignees-STARTER). **(STARTER)**
|
||||
- [Many people](#multiple-assignees-starter). **(STARTER)**
|
||||
|
||||
The assignee(s) can be changed as often as needed. The idea is that the assignees are
|
||||
responsible for that issue until it's reassigned to someone else to take it from there.
|
||||
|
|
|
@ -197,6 +197,31 @@ giving you a linear timeline of the alert's investigation and assignment history
|
|||
|
||||
![Alert Management Details View System Notes](img/alert_detail_system_notes_v13_1.png)
|
||||
|
||||
### View an Alert's metrics data
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.2.
|
||||
|
||||
To view the metrics for an alert:
|
||||
|
||||
1. Sign in as a user with Developer or higher [permissions](../../permissions.md).
|
||||
1. Navigate to **{cloud-gear}** **Operations > Alerts**.
|
||||
1. Click the alert you want to view.
|
||||
1. Below the title of the alert, click the **Metrics** tab.
|
||||
|
||||
![Alert Management Metrics View](img/alert_detail_metrics_v13_2.png)
|
||||
|
||||
For GitLab-managed Prometheus instances, metrics data is automatically available
|
||||
for the alert, making it easy to see surrounding behavior. See
|
||||
[Managed Prometheus instances](../integrations/prometheus.md#managed-prometheus-instances)
|
||||
for information on setting up alerts.
|
||||
|
||||
For externally-managed Prometheus instances, you can configure your alerting rules to
|
||||
display a chart in the alert. See
|
||||
[Embedding metrics based on alerts in incident issues](../integrations/prometheus.md#embedding-metrics-based-on-alerts-in-incident-issues)
|
||||
for information on how to appropriately configure your alerting rules. See
|
||||
[External Prometheus instances](../integrations/prometheus.md#external-prometheus-instances)
|
||||
for information on setting up alerts for your self-managed Prometheus instance.
|
||||
|
||||
## Use cases for assigning alerts
|
||||
|
||||
Consider a team formed by different sections of monitoring, collaborating on a
|
||||
|
|
BIN
doc/user/project/operations/img/alert_detail_metrics_v13_2.png
Normal file
After Width: | Height: | Size: 27 KiB |
|
@ -1,59 +1,140 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This is a GitLab-specific JSON interface. You should use this instead
|
||||
# of using `JSON` directly. This allows us to swap the adapter and handle
|
||||
# legacy issues.
|
||||
|
||||
module Gitlab
|
||||
module Json
|
||||
INVALID_LEGACY_TYPES = [String, TrueClass, FalseClass].freeze
|
||||
|
||||
class << self
|
||||
def parse(string, *args, **named_args)
|
||||
legacy_mode = legacy_mode_enabled?(named_args.delete(:legacy_mode))
|
||||
data = adapter.parse(string, *args, **named_args)
|
||||
# Parse a string and convert it to a Ruby object
|
||||
#
|
||||
# @param string [String] the JSON string to convert to Ruby objects
|
||||
# @param opts [Hash] an options hash in the standard JSON gem format
|
||||
# @return [Boolean, String, Array, Hash]
|
||||
# @raise [JSON::ParserError] raised if parsing fails
|
||||
def parse(string, opts = {})
|
||||
# First we should ensure this really is a string, not some other
|
||||
# type which purports to be a string. This handles some legacy
|
||||
# usage of the JSON class.
|
||||
string = string.to_s unless string.is_a?(String)
|
||||
|
||||
legacy_mode = legacy_mode_enabled?(opts.delete(:legacy_mode))
|
||||
data = adapter_load(string, opts)
|
||||
|
||||
handle_legacy_mode!(data) if legacy_mode
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def parse!(string, *args, **named_args)
|
||||
legacy_mode = legacy_mode_enabled?(named_args.delete(:legacy_mode))
|
||||
data = adapter.parse!(string, *args, **named_args)
|
||||
alias_method :parse!, :parse
|
||||
|
||||
handle_legacy_mode!(data) if legacy_mode
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
def dump(*args)
|
||||
adapter.dump(*args)
|
||||
# Take a Ruby object and convert it to a string
|
||||
#
|
||||
# @param object [Boolean, String, Array, Hash, Object] depending on the adapter this can be a variety of types
|
||||
# @param opts [Hash] an options hash in the standard JSON gem format
|
||||
# @return [String]
|
||||
def dump(object, opts = {})
|
||||
adapter_dump(object, opts)
|
||||
end
|
||||
|
||||
# Legacy method used in our codebase that might just be an alias for `parse`.
|
||||
# Will be updated to use our `parse` method.
|
||||
def generate(*args)
|
||||
adapter.generate(*args)
|
||||
::JSON.generate(*args)
|
||||
end
|
||||
|
||||
# Generates a JSON string and formats it nicely.
|
||||
# Varies depending on adapter and will be updated to use our methods.
|
||||
def pretty_generate(*args)
|
||||
adapter.pretty_generate(*args)
|
||||
::JSON.pretty_generate(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def adapter
|
||||
::JSON
|
||||
# Convert JSON string into Ruby through toggleable adapters.
|
||||
#
|
||||
# Must rescue adapter-specific errors and return `parser_error`, and
|
||||
# must also standardize the options hash to support each adapter as
|
||||
# they all take different options.
|
||||
#
|
||||
# @param string [String] the JSON string to convert to Ruby objects
|
||||
# @param opts [Hash] an options hash in the standard JSON gem format
|
||||
# @return [Boolean, String, Array, Hash]
|
||||
# @raise [JSON::ParserError]
|
||||
def adapter_load(string, opts = {})
|
||||
opts = standardize_opts(opts)
|
||||
|
||||
if enable_oj?
|
||||
Oj.load(string, opts)
|
||||
else
|
||||
::JSON.parse(string, opts)
|
||||
end
|
||||
rescue Oj::ParseError, Encoding::UndefinedConversionError => ex
|
||||
raise parser_error.new(ex)
|
||||
end
|
||||
|
||||
# Convert Ruby object to JSON string through toggleable adapters.
|
||||
#
|
||||
# @param object [Boolean, String, Array, Hash, Object] depending on the adapter this can be a variety of types
|
||||
# @param opts [Hash] an options hash in the standard JSON gem format
|
||||
# @return [String]
|
||||
def adapter_dump(thing, opts = {})
|
||||
opts = standardize_opts(opts)
|
||||
|
||||
if enable_oj?
|
||||
Oj.dump(thing, opts)
|
||||
else
|
||||
::JSON.dump(thing, opts)
|
||||
end
|
||||
end
|
||||
|
||||
# Take a JSON standard options hash and standardize it to work across adapters
|
||||
# An example of this is Oj taking :symbol_keys instead of :symbolize_names
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @return [Hash]
|
||||
def standardize_opts(opts = {})
|
||||
if enable_oj?
|
||||
opts[:mode] = :rails
|
||||
opts[:symbol_keys] = opts[:symbolize_keys] || opts[:symbolize_names]
|
||||
end
|
||||
|
||||
opts
|
||||
end
|
||||
|
||||
# The standard parser error we should be returning. Defined in a method
|
||||
# so we can potentially override it later.
|
||||
#
|
||||
# @return [JSON::ParserError]
|
||||
def parser_error
|
||||
::JSON::ParserError
|
||||
end
|
||||
|
||||
# @param [Nil, Boolean] an extracted :legacy_mode key from the opts hash
|
||||
# @return [Boolean]
|
||||
def legacy_mode_enabled?(arg_value)
|
||||
arg_value.nil? ? false : arg_value
|
||||
end
|
||||
|
||||
# If legacy mode is enabled, we need to raise an error depending on the values
|
||||
# provided in the string. This will be deprecated.
|
||||
#
|
||||
# @param data [Boolean, String, Array, Hash, Object]
|
||||
# @return [Boolean, String, Array, Hash, Object]
|
||||
# @raise [JSON::ParserError]
|
||||
def handle_legacy_mode!(data)
|
||||
return data unless Feature.enabled?(:json_wrapper_legacy_mode, default_enabled: true)
|
||||
|
||||
raise parser_error if INVALID_LEGACY_TYPES.any? { |type| data.is_a?(type) }
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def enable_oj?
|
||||
Feature.enabled?(:oj_json, default_enabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module Gitlab
|
|||
data.merge!(message)
|
||||
end
|
||||
|
||||
data.to_json + "\n"
|
||||
Gitlab::Json.dump(data) + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1998,6 +1998,12 @@ msgstr ""
|
|||
msgid "AlertManagement|Medium"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Metrics"
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|Metrics weren't available in the alerts payload."
|
||||
msgstr ""
|
||||
|
||||
msgid "AlertManagement|More information"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9883,7 +9889,7 @@ msgstr ""
|
|||
msgid "FeatureFlags|Edit list"
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|Enable features for specific users and specific environments by defining feature flag strategies. By default, features are available to all users in all environments."
|
||||
msgid "FeatureFlags|Enable features for specific users and specific environments by defining feature flag strategies."
|
||||
msgstr ""
|
||||
|
||||
msgid "FeatureFlags|Environment Spec"
|
||||
|
@ -10660,6 +10666,24 @@ msgstr ""
|
|||
msgid "Geo|All projects are being scheduled for reverify"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Allowed Geo IP can't be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Allowed Geo IP should be between 1 and 255 characters"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Allowed Geo IP should contain valid IP addresses"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Connection timeout can't be blank"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Connection timeout must be a number"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Connection timeout should be between 1-120"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Could not remove tracking entry for an existing project."
|
||||
msgstr ""
|
||||
|
||||
|
@ -23419,6 +23443,9 @@ msgstr ""
|
|||
msgid "There was an error trying to validate your query"
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error updating the Geo Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "There was an error updating the dashboard, branch name is invalid."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
"graphql-tag": "^2.10.1",
|
||||
"immer": "^5.2.1",
|
||||
"imports-loader": "^0.8.0",
|
||||
"ipaddr.js": "^1.9.1",
|
||||
"jed": "^1.1.1",
|
||||
"jest-transform-graphql": "^2.1.0",
|
||||
"jquery": "^3.4.1",
|
||||
|
|
|
@ -6,24 +6,33 @@ RSpec.describe 'project commit pipelines', :js do
|
|||
let(:project) { create(:project, :repository) }
|
||||
|
||||
before do
|
||||
create(:ci_pipeline, project: project,
|
||||
sha: project.commit.sha,
|
||||
ref: 'master')
|
||||
|
||||
user = create(:user)
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
visit pipelines_project_commit_path(project, project.commit.sha)
|
||||
end
|
||||
|
||||
context 'when no builds triggered yet' do
|
||||
before do
|
||||
create(:ci_pipeline, project: project,
|
||||
sha: project.commit.sha,
|
||||
ref: 'master')
|
||||
end
|
||||
|
||||
it 'user views commit pipelines page' do
|
||||
visit pipelines_project_commit_path(project, project.commit.sha)
|
||||
|
||||
it 'shows the ID of the first pipeline' do
|
||||
page.within('.table-holder') do
|
||||
expect(page).to have_content project.ci_pipelines[0].id # pipeline ids
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no related merge requests' do
|
||||
it 'shows the correct text for no related MRs' do
|
||||
wait_for_requests
|
||||
|
||||
page.within('.merge-request-info') do
|
||||
expect(page).not_to have_selector '.spinner'
|
||||
expect(page).to have_content 'No related merge requests found'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import AlertMetrics from '~/alert_management/components/alert_metrics.vue';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios';
|
||||
|
||||
jest.mock('~/monitoring/stores', () => ({
|
||||
monitoringDashboard: {},
|
||||
}));
|
||||
|
||||
const mockEmbedName = 'MetricsEmbedStub';
|
||||
|
||||
jest.mock('~/monitoring/components/embeds/metric_embed.vue', () => ({
|
||||
name: mockEmbedName,
|
||||
render(h) {
|
||||
return h('div');
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Alert Metrics', () => {
|
||||
let wrapper;
|
||||
const mock = new MockAdapter(axios);
|
||||
|
||||
function mountComponent({ props } = {}) {
|
||||
wrapper = shallowMount(AlertMetrics, {
|
||||
propsData: {
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
MetricEmbed: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const findChart = () => wrapper.find({ name: mockEmbedName });
|
||||
const findEmptyState = () => wrapper.find({ ref: 'emptyState' });
|
||||
|
||||
afterEach(() => {
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('Empty state', () => {
|
||||
it('should display a message when metrics dashboard url is not provided ', () => {
|
||||
mountComponent();
|
||||
expect(findChart().exists()).toBe(false);
|
||||
expect(findEmptyState().text()).toBe("Metrics weren't available in the alerts payload.");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Chart', () => {
|
||||
it('should be rendered when dashboard url is provided', async () => {
|
||||
mountComponent({ props: { dashboardUrl: 'metrics.url' } });
|
||||
|
||||
await waitForPromises();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findEmptyState().exists()).toBe(false);
|
||||
expect(findChart().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import initMRPage from '~/mr_notes/index';
|
||||
import initMRPage from '~/mr_notes';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { userDataMock, notesDataMock, noteableDataMock } from '../../frontend/notes/mock_data';
|
||||
import diffFileMockData from '../../frontend/diffs/mock_data/diff_file';
|
||||
import { userDataMock, notesDataMock, noteableDataMock } from '../notes/mock_data';
|
||||
import diffFileMockData from '../diffs/mock_data/diff_file';
|
||||
|
||||
export default function initVueMRPage() {
|
||||
const mrTestEl = document.createElement('div');
|
|
@ -5,7 +5,7 @@ import MergeRequestTabs from '~/merge_request_tabs';
|
|||
import '~/commit/pipelines/pipelines_bundle';
|
||||
import '~/lib/utils/common_utils';
|
||||
import 'vendor/jquery.scrollTo';
|
||||
import initMrPage from '../javascripts/helpers/init_vue_mr_page_helper';
|
||||
import initMrPage from 'helpers/init_vue_mr_page_helper';
|
||||
|
||||
jest.mock('~/lib/utils/webpack', () => ({
|
||||
resetServiceWorkersPublicPath: jest.fn(),
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
export default class ClassSpecHelper {
|
||||
static itShouldBeAStaticMethod(base, method) {
|
||||
return it('should be a static method', () => {
|
||||
expect(Object.prototype.hasOwnProperty.call(base, method)).toBeTruthy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.ClassSpecHelper = ClassSpecHelper;
|
|
@ -1 +0,0 @@
|
|||
export { default } from '../../frontend/helpers/filtered_search_spec_helper';
|
|
@ -1,3 +0,0 @@
|
|||
import mountComponent, { mountComponentWithStore } from './vue_mount_component_helper';
|
||||
|
||||
export { mountComponent, mountComponentWithStore };
|
|
@ -1,11 +0,0 @@
|
|||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
export const setLanguage = languageCode => {
|
||||
const htmlElement = document.querySelector('html');
|
||||
|
||||
if (languageCode) {
|
||||
htmlElement.setAttribute('lang', languageCode);
|
||||
} else {
|
||||
htmlElement.removeAttribute('lang');
|
||||
}
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
export default (time = 0) =>
|
||||
new Promise(resolve => {
|
||||
setTimeout(resolve, time);
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* Replaces line break with an empty space
|
||||
* @param {*} data
|
||||
*/
|
||||
export const removeBreakLine = data => data.replace(/\r?\n|\r/g, ' ');
|
||||
|
||||
/**
|
||||
* Removes line breaks, spaces and trims the given text
|
||||
* @param {String} str
|
||||
* @returns {String}
|
||||
*/
|
||||
export const trimText = str =>
|
||||
str
|
||||
.replace(/\r?\n|\r/g, '')
|
||||
.replace(/\s\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
export const removeWhitespace = str => str.replace(/\s\s+/g, ' ');
|
|
@ -1,5 +0,0 @@
|
|||
// No new code should be added to this file. Instead, modify the
|
||||
// file this one re-exports from. For more detail about why, see:
|
||||
// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349
|
||||
|
||||
export * from '../../frontend/helpers/tracking_helper';
|
|
@ -1,14 +0,0 @@
|
|||
export default {
|
||||
createNumberRandomUsers(numberUsers) {
|
||||
const users = [];
|
||||
for (let i = 0; i < numberUsers; i += 1) {
|
||||
users.push({
|
||||
avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
|
||||
id: i + 1,
|
||||
name: `GitLab User ${i}`,
|
||||
username: `gitlab${i}`,
|
||||
});
|
||||
}
|
||||
return users;
|
||||
},
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from '../../frontend/helpers/vue_mount_component_helper';
|
||||
export * from '../../frontend/helpers/vue_mount_component_helper';
|
|
@ -1,5 +0,0 @@
|
|||
// No new code should be added to this file. Instead, modify the
|
||||
// file this one re-exports from. For more detail about why, see:
|
||||
// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349
|
||||
|
||||
export * from '../../frontend/helpers/vue_test_utils_helper';
|
|
@ -1,102 +0,0 @@
|
|||
const noop = () => {};
|
||||
|
||||
/**
|
||||
* Helper for testing action with expected mutations inspired in
|
||||
* https://vuex.vuejs.org/en/testing.html
|
||||
*
|
||||
* @param {Function} action to be tested
|
||||
* @param {Object} payload will be provided to the action
|
||||
* @param {Object} state will be provided to the action
|
||||
* @param {Array} [expectedMutations=[]] mutations expected to be committed
|
||||
* @param {Array} [expectedActions=[]] actions expected to be dispatched
|
||||
* @param {Function} [done=noop] to be executed after the tests
|
||||
* @return {Promise}
|
||||
*
|
||||
* @example
|
||||
* testAction(
|
||||
* actions.actionName, // action
|
||||
* { }, // mocked payload
|
||||
* state, //state
|
||||
* // expected mutations
|
||||
* [
|
||||
* { type: types.MUTATION}
|
||||
* { type: types.MUTATION_1, payload: jasmine.any(Number)}
|
||||
* ],
|
||||
* // expected actions
|
||||
* [
|
||||
* { type: 'actionName', payload: {param: 'foobar'}},
|
||||
* { type: 'actionName1'}
|
||||
* ]
|
||||
* done,
|
||||
* );
|
||||
*
|
||||
* @example
|
||||
* testAction(
|
||||
* actions.actionName, // action
|
||||
* { }, // mocked payload
|
||||
* state, //state
|
||||
* [ { type: types.MUTATION} ], // expected mutations
|
||||
* [], // expected actions
|
||||
* ).then(done)
|
||||
* .catch(done.fail);
|
||||
*/
|
||||
export default (
|
||||
action,
|
||||
payload,
|
||||
state,
|
||||
expectedMutations = [],
|
||||
expectedActions = [],
|
||||
done = noop,
|
||||
) => {
|
||||
const mutations = [];
|
||||
const actions = [];
|
||||
|
||||
// mock commit
|
||||
const commit = (type, mutationPayload) => {
|
||||
const mutation = { type };
|
||||
|
||||
if (typeof mutationPayload !== 'undefined') {
|
||||
mutation.payload = mutationPayload;
|
||||
}
|
||||
|
||||
mutations.push(mutation);
|
||||
};
|
||||
|
||||
// mock dispatch
|
||||
const dispatch = (type, actionPayload) => {
|
||||
const dispatchedAction = { type };
|
||||
|
||||
if (typeof actionPayload !== 'undefined') {
|
||||
dispatchedAction.payload = actionPayload;
|
||||
}
|
||||
|
||||
actions.push(dispatchedAction);
|
||||
};
|
||||
|
||||
const validateResults = () => {
|
||||
expect({
|
||||
mutations,
|
||||
actions,
|
||||
}).toEqual({
|
||||
mutations: expectedMutations,
|
||||
actions: expectedActions,
|
||||
});
|
||||
done();
|
||||
};
|
||||
|
||||
const result = action(
|
||||
{ commit, state, dispatch, rootState: state, rootGetters: state, getters: state },
|
||||
payload,
|
||||
);
|
||||
|
||||
return new Promise(setImmediate)
|
||||
.then(() => result)
|
||||
.catch(error => {
|
||||
validateResults();
|
||||
throw error;
|
||||
})
|
||||
.then(data => {
|
||||
validateResults();
|
||||
return data;
|
||||
});
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export default () => new Promise(resolve => requestAnimationFrame(resolve));
|
|
@ -1,2 +0,0 @@
|
|||
export { default } from '../../frontend/jobs/mock_data';
|
||||
export * from '../../frontend/jobs/mock_data';
|
|
@ -1,61 +1,6 @@
|
|||
import pixelmatch from 'pixelmatch';
|
||||
|
||||
export default {
|
||||
toContainText: () => ({
|
||||
compare(vm, text) {
|
||||
if (!(vm.$el instanceof HTMLElement)) {
|
||||
throw new Error('vm.$el is not a DOM element!');
|
||||
}
|
||||
|
||||
const result = {
|
||||
pass: vm.$el.innerText.includes(text),
|
||||
};
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
toHaveSpriteIcon: () => ({
|
||||
compare(element, iconName) {
|
||||
if (!iconName) {
|
||||
throw new Error('toHaveSpriteIcon is missing iconName argument!');
|
||||
}
|
||||
|
||||
if (!(element instanceof HTMLElement)) {
|
||||
throw new Error(`${element} is not a DOM element!`);
|
||||
}
|
||||
|
||||
const iconReferences = [].slice.apply(element.querySelectorAll('svg use'));
|
||||
const matchingIcon = iconReferences.find(reference =>
|
||||
reference.getAttribute('xlink:href').endsWith(`#${iconName}`),
|
||||
);
|
||||
const result = {
|
||||
pass: Boolean(matchingIcon),
|
||||
};
|
||||
|
||||
if (result.pass) {
|
||||
result.message = `${element.outerHTML} contains the sprite icon "${iconName}"!`;
|
||||
} else {
|
||||
result.message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`;
|
||||
|
||||
const existingIcons = iconReferences.map(reference => {
|
||||
const iconUrl = reference.getAttribute('xlink:href');
|
||||
return `"${iconUrl.replace(/^.+#/, '')}"`;
|
||||
});
|
||||
if (existingIcons.length > 0) {
|
||||
result.message += ` (only found ${existingIcons.join(',')})`;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
toRender: () => ({
|
||||
compare(vm) {
|
||||
const result = {
|
||||
pass: vm.$el.nodeType !== Node.COMMENT_NODE,
|
||||
};
|
||||
return result;
|
||||
},
|
||||
}),
|
||||
toImageDiffEqual: () => {
|
||||
const getImageData = img => {
|
||||
const canvas = document.createElement('canvas');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Parsers::Accessibility::Pa11y do
|
||||
describe '#parse!' do
|
||||
|
@ -108,7 +108,7 @@ RSpec.describe Gitlab::Ci::Parsers::Accessibility::Pa11y do
|
|||
it "sets error_message" do
|
||||
expect { subject }.not_to raise_error
|
||||
|
||||
expect(accessibility_report.error_message).to include('Pa11y parsing failed')
|
||||
expect(accessibility_report.error_message).to include('JSON parsing failed')
|
||||
expect(accessibility_report.errors_count).to eq(0)
|
||||
expect(accessibility_report.passes_count).to eq(0)
|
||||
expect(accessibility_report.scans_count).to eq(0)
|
||||
|
|
|
@ -30,7 +30,7 @@ RSpec.describe Gitlab::PhabricatorImport::Conduit::Response do
|
|||
body: 'This is no JSON')
|
||||
|
||||
expect { described_class.parse!(fake_response) }
|
||||
.to raise_error(Gitlab::PhabricatorImport::Conduit::ResponseError, /unexpected token at/)
|
||||
.to raise_error(Gitlab::PhabricatorImport::Conduit::ResponseError, /unexpected character/)
|
||||
end
|
||||
|
||||
it 'returns a parsed response for valid input' do
|
||||
|
|
|
@ -4781,9 +4781,21 @@ RSpec.describe User do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'bot user avatars' do |bot_type, avatar_filename|
|
||||
it 'sets the custom avatar for the created bot' do
|
||||
bot_user = described_class.public_send(bot_type)
|
||||
|
||||
expect(bot_user.avatar.url).to be_present
|
||||
expect(bot_user.avatar.filename).to eq(avatar_filename)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'bot users', :alert_bot
|
||||
it_behaves_like 'bot users', :support_bot
|
||||
it_behaves_like 'bot users', :migration_bot
|
||||
it_behaves_like 'bot users', :ghost
|
||||
|
||||
it_behaves_like 'bot user avatars', :alert_bot, 'alert-bot.png'
|
||||
it_behaves_like 'bot user avatars', :support_bot, 'support-bot.png'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6130,11 +6130,16 @@ ip@^1.1.0, ip@^1.1.5:
|
|||
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
|
||||
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
|
||||
|
||||
ipaddr.js@1.9.0, ipaddr.js@^1.9.0:
|
||||
ipaddr.js@1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
|
||||
integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
|
||||
|
||||
ipaddr.js@^1.9.0, ipaddr.js@^1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
||||
|
||||
is-absolute-url@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
|
||||
|
|