提交 f67f18d9 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 4a14cfd1
......@@ -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}
......
......@@ -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)
......
......@@ -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"
......
<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
}
}
}
<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
......
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();
});
# 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')
# 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
......
- 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')
---
title: Add custom avatars for Alert and Support Bot
merge_request: 36269
author:
type: added
---
title: Surface metrics charts on the alert detail page
merge_request: 35044
author:
type: added
---
title: Fix infinite loading spinner for related merge requests on commit pipelines
tab
merge_request: 36077
author:
type: fixed
---
title: Add oj gem for faster JSON
merge_request: 35527
author:
type: performance
# 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)
# 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
......
# 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**
......
......@@ -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
......
......@@ -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:
......
......@@ -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
......
# 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)
handle_legacy_mode!(data) if legacy_mode
data
end
alias_method :parse!, :parse
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 ""
......
......@@ -6,23 +6,32 @@ 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')
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
it 'user views commit pipelines page' do
visit pipelines_project_commit_path(project, project.commit.sha)
context 'with no related merge requests' do
it 'shows the correct text for no related MRs' do
wait_for_requests
page.within('.table-holder') do
expect(page).to have_content project.ci_pipelines[0].id # pipeline ids
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
......
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);
});
});
});
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(),
......
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;
export { default } from '../../frontend/helpers/filtered_search_spec_helper';
import mountComponent, { mountComponentWithStore } from './vue_mount_component_helper';
export { mountComponent, mountComponentWithStore };
/* 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');
}
};
export default (time = 0) =>
new Promise(resolve => {
setTimeout(resolve, time);
});
/**
* 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, ' ');
// 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';
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;
},
};
export { default } from '../../frontend/helpers/vue_mount_component_helper';
export * from '../../frontend/helpers/vue_mount_component_helper';
// 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';
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;
});
};
export default () => new Promise(resolve => requestAnimationFrame(resolve));
export { default } from '../../frontend/jobs/mock_data';
export * from '../../frontend/jobs/mock_data';
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');
......
# 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"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册